一文搞懂Spring事务传播行为(Transaction Propagation)

1,063 阅读19分钟

事务的传播行为是为了解决业务层方法之间互相调用的事务问题,当一个事务方法被另一事务方法调用时,不同的传播行为会产生不同的结果。本文将结合代码实例,探讨一下如何正确使用事务的传播行为。

说明:本篇内容基于 MySQL(8.0.30-1.el8)及 SpringBoot(3.3.5)进行测试。

事务传播行为

Spring通过Propagation枚举类定义了7种传播行为。

传播行为说明解释
REQUIREDSupport a current transaction, create a new one if none exists. Analogous to EJB transaction attribute of the same name.
This is the default setting of a transaction annotation.
如果当前没有事务,就新建一个事务;如果当前存在事务,就加入到这个事务中。
这是默认的事务传播行为。
SUPPROTSSupport a current transaction, execute non-transactionally if none exists. Analogous to EJB transaction attribute of the same name.
Note: For transaction managers with transaction synchronization, SUPPORTS is slightly different from no transaction at all, as it defines a transaction scope that synchronization will apply for. As a consequence, the same resources (JDBC Connection, Hibernate Session, etc) will be shared for the entire specified scope. Note that this depends on the actual synchronization configuration of the transaction manager.
如果当前没有事务,就以非事务方式执行;如果当前存在事务,就加入到这个事务中。
MANADATORYSupport a current transaction, throw an exception if none exists. Analogous to EJB transaction attribute of the same name.如果当前没有事务,就抛出异常;如果当前存在事务,就加入到这个事务中。
REQUIRES_NEWCreate a new transaction, and suspend the current transaction if one exists. Analogous to the EJB transaction attribute of the same name.
NOTE: Actual transaction suspension will not work out-of-the-box on all transaction managers. This in particular applies to org.springframework.transaction.jta.JtaTransactionManager, which requires the jakarta.transaction.TransactionManager to be made available to it (which is server-specific in standard Jakarta EE).
如果当前没有事务,就新建一个事务;如果当前存在事务,就挂起当前事务并新建一个事务。
NOT_SUPPORTEDExecute non-transactionally, suspend the current transaction if one exists. Analogous to EJB transaction attribute of the same name.
NOTE: Actual transaction suspension will not work out-of-the-box on all transaction managers. This in particular applies to org.springframework.transaction.jta.JtaTransactionManager, which requires the jakarta.transaction.TransactionManager to be made available to it (which is server-specific in standard Jakarta EE).
以非事务方式执行;如果当前存在事务,就挂起当前事务。
NEVERExecute non-transactionally, throw an exception if a transaction exists. Analogous to EJB transaction attribute of the same name.以非事务方式执行;如果当前存在事务,就抛出异常。
NESTEDExecute within a nested transaction if a current transaction exists, behave like REQUIRED otherwise. There is no analogous feature in EJB.
Note: Actual creation of a nested transaction will only work on specific transaction managers. Out of the box, this only applies to the JDBC DataSourceTransactionManager. Some JTA providers might support nested transactions as well.
如果当前没有事务,就新建一个事务(类似于REQUIRED);如果当前存在事务,则作为当前事务的子事务执行。

代码案例

接下来我们通过代码案例充分了解一下这些传播行为的用法。

先定义两个内部服务PropagationAddUser1ServicePropagationAddUser2Service,用于实现数据库的插入。

@Service
public class PropagationAddUser1ServiceImpl implements PropagationAddUser1Service {

    @Resource
    private User1Mapper user1Mapper;
    
    // 不使用事务插入
    @Override
    public void add(User1 user) {
        user1Mapper.insert(user);
    }
    
    // 其他方法...
}
@Service
public class PropagationAddUser2ServiceImpl implements PropagationAddUser2Service {

    @Resource
    private User2Mapper user2Mapper;
    
    // 不使用事务插入
    @Override
    public void add(User2 user) {
        user2Mapper.insert(user);
    }
    
    // 不使用事务插入,抛出异常
    @Override
    public void addThrow(User2 user) {
        user2Mapper.insert(user);
        // 模拟异常
        throw new RuntimeException();
    }
    
    // 其他方法...
}

再定义一个外部服务PropagationAddExampleImpl,用于实现外部方法调用内部服务的方法。

@Service
public class PropagationAddExampleImpl implements PropagationAddExample {
    
    private final PropagationAddUser1Service user1Service;
    private final PropagationAddUser2Service user2Service;
    
    @Autowired
    public PropagationAddExampleImpl(PropagationAddUser1Service user1Service, PropagationAddUser2Service user2Service) {
        this.user1Service = user1Service;
        this.user2Service = user2Service;
    }
    
    // 外部方法出现异常
    @Override
    public void noEx_no_no() {
        user1Service.add(createUser1());
        user2Service.add(createUser2());
        // 模拟异常
        throw new RuntimeException();
    }

    // 内部方法出现异常
    @Override
    public void no_no_noEx() {
        user1Service.add(createUser1());
        user2Service.addThrow(createUser2());
    }
    
    // 其他方法...
}

最终通过调用外部服务来测试结果。

比如上述两个外部方法noEx_no_nono_no_noEx调用后的结果都为成功,因为没有使用事务,所以不会触发回滚。对应的测试用例如下:

@Test
void noEx_no_no() {
    assertThrows(RuntimeException.class, propagationAddExample::noEx_no_no);
    assertEquals(1, getUser1Count());
    assertEquals(1, getUser2Count());
}

@Test
void no_no_noEx() {
    assertThrows(RuntimeException.class, propagationAddExample::no_no_noEx);
    assertEquals(1, getUser1Count());
    assertEquals(1, getUser2Count());
}

上述外部方法,如果添加了@Transactional注解,即开启默认事务,此时整个外部方法将视为一个事务,出现异常会触发事务回滚,比如下方内部方法1和内部方法2产生的数据都会回滚。

// 外部方法出现异常
@Transactional
@Override
public void requiredEx_no_no() {
    user1Service.add(createUser1());
    user2Service.add(createUser2());
    // 模拟异常
    throw new RuntimeException();
}

// 内部方法出现异常
@Transactional
@Override
public void required_no_noEx() {
    user1Service.add(createUser1());
    user2Service.addThrow(createUser2());
}

这也是我们开发中最常见的使用方式,而通过传播行为可以实现更为复杂的业务场景。

1. REQUIRED

如果当前没有事务,就新建一个事务;如果当前存在事务,就加入到这个事务中。

我们平时使用的@Transactional注解默认就是使用此传播行为。

@Service
public class PropagationAddUser1ServiceImpl implements PropagationAddUser1Service {

    // 使用事务插入
    @Transactional(propagation = Propagation.REQUIRED)
    @Override
    public void addRequired(User1 user) {
        user1Mapper.insert(user);
    }
    
    // 其他方法...
}
@Service
public class PropagationAddUser2ServiceImpl implements PropagationAddUser2Service {

    // 使用事务插入
    @Transactional(propagation = Propagation.REQUIRED)
    @Override
    public void addRequired(User2 user) {
        user2Mapper.insert(user);
    }
    
    // 使用事务插入,抛出异常
    @Transactional(propagation = Propagation.REQUIRED)
    @Override
    public void addRequiredThrow(User2 user) {
        user2Mapper.insert(user);
        // 模拟异常
        throw new RuntimeException();
    }
    
    // 其他方法...
}

1.1 场景1:外部方法没有开启事务

1.1.1 外部方法出现异常
@Override
public void noEx_required_required() {
    user1Service.addRequired(createUser1());
    user2Service.addRequired(createUser2());
    throw new RuntimeException();
}
服务结果分析
服务1成功外部方法没有事务,外部方法出现的异常不会触发服务1回滚
服务2成功外部方法没有事务,外部方法出现的异常不会触发服务2回滚
1.1.2 内部方法出现异常
@Override
public void no_required_requiredEx() {
    user1Service.addRequired(createUser1());
    user2Service.addRequiredThrow(createUser2());
}
服务结果分析
服务1成功服务1与服务2事务相互独立,服务2异常不会触发服务1回滚
服务2回滚服务2存在事务,出现异常触发回滚

1.2 场景2:外部方法开启事务

1.2.1 外部方法出现异常
@Transactional
@Override
public void requiredEx_required_required() {
    user1Service.addRequired(createUser1());
    user2Service.addRequiredThrow(createUser2());
    throw new RuntimeException();
}
服务结果分析
服务1回滚外部方法存在事务,服务1加入该事务中,为同一个事务,外部方法异常会触发服务1回滚
服务2回滚外部方法存在事务,服务2加入该事务中,为同一个事务,外部方法异常会触发服务2回滚
1.2.2 内部方法出现异常
@Transactional
@Override
public void required_required_requiredEx() {
    user1Service.addRequired(createUser1());
    user2Service.addRequiredThrow(createUser2());
}
服务结果分析
服务1回滚外部方法存在事务,服务1加入该事务中,为同一个事务,服务2异常会触发服务1回滚
服务2回滚服务2存在事务,出现异常触发回滚
1.2.3 捕获内部方法的异常
@Transactional
@Override
public void required_required_requiredCatch() {
    user1Service.addRequired(createUser1());
    try {
        user2Service.addRequiredThrow(createUser2());
    } catch (Exception ignored) {}
}
服务结果分析
服务1回滚外部方法存在事务,服务1加入该事务中,此时因异常拦截导致外部方法无法感知到异常,但事务已经标记为回滚了,所以外部事务提交时会触发org.springframework.transaction.UnexpectedRollbackException: Transaction rolled back because it has been marked as rollback-only,表示已执行回滚
服务2回滚服务2存在事务,出现异常触发回滚

Spring官方关于UnexpectedRollbackException的描述:

However, in the case where an inner transaction scope sets the rollback-only marker, the outer transaction has not decided on the rollback itself, so the rollback (silently triggered by the inner transaction scope) is unexpected. A corresponding UnexpectedRollbackException is thrown at that point. This is expected behavior so that the caller of a transaction can never be misled to assume that a commit was performed when it really was not. So, if an inner transaction (of which the outer caller is not aware) silently marks a transaction as rollback-only, the outer caller still calls commit. The outer caller needs to receive an UnexpectedRollbackException to indicate clearly that a rollback was performed instead.

点击查看原文

简单翻译一下:

在内部事务范围设置了仅回滚标记的情况下,外部事务本身并没有决定回滚,所以回滚(由内部事务静默触发的)是意外的,此时会抛出UnexpectedRollbackException。因此,如果一个内部事务默默地将一个事务标记为仅回滚,外部调用者仍然会提交,此时外部调用者需要收到一个意外回滚的异常,以明确表示已执行回滚。

2. REQUIRES_NEW

如果当前没有事务,就新建一个事务;如果当前存在事务,就挂起当前事务并新建一个事务。

新建如下方法,@Transactional注解的propagation参数设置为Propagation.REQUIRES_NEW。(方法实现内容不变,此处省略,后续不再赘述)

// 使用事务插入
@Transactional(propagation = Propagation.REQUIRES_NEW)
@Override
public void addRequiresNew(User1 user) {...}
// 使用事务插入
@Transactional(propagation = Propagation.REQUIRES_NEW)
@Override
public void addRequiresNew(User2 user) {...}

// 使用事务插入,抛出异常
@Transactional(propagation = Propagation.REQUIRES_NEW)
@Override
public void addRequiresNewThrow(User2 user) {...}

2.1 场景1:外部方法没有开启事务

2.1.1 外部方法出现异常
@Override
public void noEx_new_new() {
    user1Service.addRequiresNew(createUser1());
    user2Service.addRequiresNew(createUser2());
    throw new RuntimeException();
}
服务结果分析
服务1成功外部方法没有事务,外部方法出现的异常不会触发服务1回滚
服务2成功外部方法没有事务,外部方法出现的异常不会触发服务2回滚
2.1.2 内部方法出现异常
@Override
public void no_new_newEx() {
    user1Service.addRequiresNew(createUser1());
    user2Service.addRequiresNewThrow(createUser2());
}
服务结果分析
服务1成功服务1与服务2事务相互独立,服务2异常不会触发服务1回滚
服务2回滚服务2存在事务,出现异常触发回滚

2.2 场景2:外部方法开启事务

2.2.1 外部方法出现异常
@Transactional
@Override
public void requiredEx_new_new() {
    user1Service.addRequiresNew(createUser1());
    user2Service.addRequiresNew(createUser2());
    throw new RuntimeException();
}
服务结果分析
服务1成功服务1为新建的事务,与外部事务互不干扰,外部服务异常不会导致服务1回滚
服务2成功服务2为新建的事务,与外部事务互不干扰,外部服务异常不会导致服务2回滚
2.2.2 内部方法出现异常
@Transactional
@Override
public void required_new_newEx() {
    user1Service.addRequiresNew(createUser1());
    user2Service.addRequiresNewThrow(createUser2());
}
服务结果分析
服务1成功服务1为新建的事务,与其他事务互不干扰,服务2异常不会导致服务1回滚
服务2回滚服务2存在事务,出现异常触发回滚
2.2.3 捕获内部方法的异常
@Transactional
@Override
public void required_new_newCatch() {
    user1Service.addRequiresNew(createUser1());
    try {
        user2Service.addRequiresNewThrow(createUser2());
    } catch (Exception ignored) {}
}
服务结果分析
服务1成功服务1为新建的事务,与其他事务互不干扰,服务2异常不会导致服务1回滚
服务2回滚服务2存在事务,出现异常触发回滚

2.3 注意事项

Spring官方针对REQUIRES_NEW的说明:

The resources attached to the outer transaction will remain bound there while the inner transaction acquires its own resources such as a new database connection. This may lead to exhaustion of the connection pool and potentially to a deadlock if several threads have an active outer transaction and wait to acquire a new connection for their inner transaction, with the pool not being able to hand out any such inner connection anymore. Do not use PROPAGATION_REQUIRES_NEW unless your connection pool is appropriately sized, exceeding the number of concurrent threads by at least 1.

点击查看原文

简单解释一下:

当内部事务获取自己的资源,如新的数据库连接时,外部事务的资源将保存绑定状态。也就是说,使用REQUIRES_NEW会导致一个请求占用多个数据库连接,可能会导致连接池资源耗尽,并且可能导致死锁。除非连接池大小至少超过并发线程数,否则不要使用REQUIRES_NEW

3. NESTED

如果当前没有事务,就新建一个事务(类似于REQUIRED);如果当前存在事务,则作为当前事务的子事务执行。

新建如下方法,@Transactional注解的propagation参数设置为Propagation.NESTED

// 使用事务插入
@Transactional(propagation = Propagation.NESTED)
@Override
public void addNested(User1 user) {...}
// 使用事务插入
@Transactional(propagation = Propagation.REQUIRES_NEW)
@Override
public void addNested(User2 user) {...}

// 使用事务插入,抛出异常
@Transactional(propagation = Propagation.REQUIRES_NEW)
@Override
public void addNestedThrow(User2 user) {...}

3.1 场景1:外部方法没有开启事务

3.1.1 外部方法出现异常
@Override
public void noEx_nested_nested() {
    user1Service.addNested(createUser1());
    user2Service.addNested(createUser2());
    throw new RuntimeException();
}
服务结果分析
服务1成功外部方法没有事务,嵌套事务不成立,执行与REQUIRED类似的操作,不会回滚
服务2成功外部方法没有事务,嵌套事务不成立,执行与REQUIRED类似的操作,不会回滚
3.1.2 内部方法出现异常
@Override
public void no_nested_nestedEx() {
    user1Service.addNested(createUser1());
    user2Service.addNestedThrow(createUser2());
}
服务结果分析
服务1成功外部方法没有事务,嵌套事务不成立,执行与REQUIRED类似的操作,不会回滚
服务2回滚外部方法没有事务,嵌套事务不成立,执行与REQUIRED类似的操作,异常触发回滚

3.2 场景2:外部方法开启事务

3.2.1 外部方法出现异常
@Transactional
@Override
public void requiredEx_nested_nested() {
    user1Service.addNested(createUser1());
    user2Service.addNested(createUser2());
    throw new RuntimeException();
}
服务结果分析
服务1回滚外部方法存在事务,服务1将作为外部方法的子事务,外部方法异常触发回滚,子事务会同步回滚
服务2回滚外部方法存在事务,服务2将作为外部方法的子事务,外部方法异常触发回滚,子事务会同步回滚
3.2.2 内部方法出现异常
@Transactional
@Override
public void required_nested_nestedEx() {
    user1Service.addNested(createUser1());
    user2Service.addNestedThrow(createUser2());
}
服务结果分析
服务1回滚外部方法存在事务,服务1将作为外部方法的子事务,服务2的异常被外部事务捕获到触发回滚,服务1作为外部事务的子事务会同步回滚
服务2回滚服务2存在事务,出现异常触发回滚
3.2.3 捕获内部方法的异常
@Transactional
@Override
public void required_nested_nestedCatch() {
    user1Service.addNested(createUser1());
    try {
        user2Service.addNestedThrow(createUser2());
    } catch (Exception ignored) {}
}
服务结果分析
服务1成功外部方法存在事务,服务1将作为外部方法的子事务,服务2的异常被捕获了,外部事务无法感知到异常不会触发回滚,所以服务1作为子事务也不会回滚
服务2回滚服务2存在事务,出现异常触发回滚,但异常被拦截,外部事务不会触发回滚

3.3 注意事项

Spring官方针对NESTED的说明:

PROPAGATION_NESTED uses a single physical transaction with multiple savepoints that it can roll back to. Such partial rollbacks let an inner transaction scope trigger a rollback for its scope, with the outer transaction being able to continue the physical transaction despite some operations having been rolled back. This setting is typically mapped onto JDBC savepoints, so it works only with JDBC resource transactions. See Spring’s DataSourceTransactionManager.

点击查看原文

此内容中需要注意的是,NESTED仅适用于JDBC资源事务。

3.4 NESTED 与 REQUIRED 对比

传播行为NESTEDREQUIRED的测试结果有许多相似之处,但区别在于REQUIRED是同一事务,NESTED是嵌套事务。

换句话说,如果REQUIED的事务出现异常,同一事务内的所有数据都会回滚;而NESTED的父事务回滚会导致子事务回滚,子事务回滚不会导致父事务回滚(子事务的异常没有抛出给父事务就不会回滚)。

4 SUPPORTS

如果当前没有事务,就以非事务方式执行;如果当前存在事务,就加入到这个事务中。

新建如下方法,@Transactional注解的propagation参数设置为Propagation.SUPPORTS

// 使用事务插入
@Transactional(propagation = Propagation.SUPPORTS)
@Override
public void addSupports(User1 user) {...}
// 使用事务插入
@Transactional(propagation = Propagation.SUPPORTS)
@Override
public void addSupports(User2 user) {...}

// 使用事务插入,抛出异常
@Transactional(propagation = Propagation.SUPPORTS)
@Override
public void addSupportsThrow(User2 user) {...}

4.1 场景1:外部方法没有开启事务

4.1.1 外部方法出现异常
@Override
public void noEx_supports_supports() {
    user1Service.addSupports(createUser1());
    user2Service.addSupports(createUser2());
    throw new RuntimeException();
}
服务结果分析
服务1成功外部方法没有事务,以非事务形式执行,不会回滚
服务2成功外部方法没有事务,以非事务形式执行,不会回滚
4.1.2 内部方法出现异常
@Override
public void no_supports_supportsEx() {
    user1Service.addSupports(createUser1());
    user2Service.addSupportsThrow(createUser2());
}
服务结果分析
服务1成功外部方法没有事务,以非事务形式执行,不会回滚
服务2成功外部方法没有事务,以非事务形式执行,不会回滚

4.2 场景2:外部方法开启事务

4.2.1 外部方法出现异常
@Transactional
@Override
public void requiredEx_supports_supports() {
    user1Service.addSupports(createUser1());
    user2Service.addSupports(createUser2());
    throw new RuntimeException();
}
服务结果分析
服务1回滚外部方法存在事务,服务1将使用该事务,以该事务的规则执行,REQUIRED的规则在此时会触发回滚
服务2回滚外部方法存在事务,服务2将使用该事务,以该事务的规则执行,REQUIRED的规则在此时会触发回滚
4.2.2 内部方法出现异常
@Transactional
@Override
public void required_supports_supportsEx() {
    user1Service.addSupports(createUser1());
    user2Service.addSupportsThrow(createUser2());
}
服务结果分析
服务1回滚外部方法存在事务,服务1将使用该事务,以该事务的规则执行,REQUIRED的规则在此时会触发回滚
服务2回滚外部方法存在事务,服务2将使用该事务,以该事务的规则执行,REQUIRED的规则在此时会触发回滚
4.2.3 捕获内部方法的异常
@Transactional
@Override
public void required_supports_supportsCatch() {
    user1Service.addSupports(createUser1());
    try {
        user2Service.addSupportsThrow(createUser2());
    } catch (Exception ignored) {}
}
服务结果分析
服务1回滚外部方法存在事务,服务1将使用该事务,以该事务的规则执行,REQUIRED的规则在此时会触发回滚
服务2回滚外部方法存在事务,服务2将使用该事务,以该事务的规则执行,REQUIRED的规则在此时会触发回滚

5 MANDATORY

如果当前没有事务,就抛出异常;如果当前存在事务,就加入到这个事务中。

新建如下方法,@Transactional注解的propagation参数设置为Propagation.MANDATORY

// 使用事务插入
@Transactional(propagation = Propagation.MANDATORY)
@Override
public void addMandatory(User1 user) {...}
// 使用事务插入
@Transactional(propagation = Propagation.MANDATORY)
@Override
public void addMandatory(User2 user) {...}

// 使用事务插入,抛出异常
@Transactional(propagation = Propagation.MANDATORY)
@Override
public void addMandatoryThrow(User2 user) {...}

5.1 场景1:外部方法没有开启事务

@Override
public void no_mandatory_mandatory() {
    user1Service.addMandatory(createUser1());
    user2Service.addMandatory(createUser2());
}
服务结果分析
服务1异常外部方法没有事务,直接抛出异常,不会执行
服务2异常外部方法没有事务,直接抛出异常,不会执行

此时抛出的异常为:org.springframework.transaction.IllegalTransactionStateException: No existing transaction found for transaction marked with propagation 'mandatory'

5.2 场景2:外部方法开启事务

5.2.1 外部方法出现异常
@Transactional
@Override
public void requiredEx_mandatory_mandatory() {
    user1Service.addMandatory(createUser1());
    user2Service.addMandatory(createUser2());
    throw new RuntimeException();
}
服务结果分析
服务1回滚外部方法存在事务,服务1将使用该事务,以该事务的规则执行,REQUIRED的规则在此时会触发回滚
服务2回滚外部方法存在事务,服务2将使用该事务,以该事务的规则执行,REQUIRED的规则在此时会触发回滚
5.2.2 内部方法出现异常
@Transactional
@Override
public void required_mandatory_mandatoryEx() {
    user1Service.addMandatory(createUser1());
    user2Service.addMandatoryThrow(createUser2());
}
服务结果分析
服务1回滚外部方法存在事务,服务1将使用该事务,以该事务的规则执行,REQUIRED的规则在此时会触发回滚
服务2回滚外部方法存在事务,服务2将使用该事务,以该事务的规则执行,REQUIRED的规则在此时会触发回滚
5.2.3 捕获内部方法的异常
@Transactional
@Override
public void required_mandatory_mandatoryCatch() {
    user1Service.addMandatory(createUser1());
    try {
        user2Service.addMandatoryThrow(createUser2());
    } catch (Exception ignored) {}
}
服务结果分析
服务1回滚外部方法存在事务,服务1将使用该事务,以该事务的规则执行,REQUIRED的规则在此时会触发回滚
服务2回滚外部方法存在事务,服务2将使用该事务,以该事务的规则执行,REQUIRED的规则在此时会触发回滚

6 NOT_SUPPORTED

以非事务方式执行;如果当前存在事务,就挂起当前事务。

新建如下方法,@Transactional注解的propagation参数设置为Propagation.NOT_SUPPORTED

// 使用事务插入
@Transactional(propagation = Propagation.NOT_SUPPORTED)
@Override
public void addNotSupported(User1 user) {...}
// 使用事务插入
@Transactional(propagation = Propagation.NOT_SUPPORTED)
@Override
public void addNotSupported(User2 user) {...}

// 使用事务插入,抛出异常
@Transactional(propagation = Propagation.NOT_SUPPORTED)
@Override
public void addNotSupportedThrow(User2 user) {...}

6.1 场景1:外部方法没有开启事务

此时直接以非事务方式执行,不再赘述。

6.2 场景2:外部方法开启事务

6.2.1 外部方法出现异常
@Transactional
@Override
public void requiredEx_not_not() {
    user1Service.addNotSupported(createUser1());
    user2Service.addNotSupported(createUser2());
    throw new RuntimeException();
}
服务结果分析
服务1成功外部方法存在事务,将挂起当前事务,服务1会以非事务方式执行,不会回滚
服务2成功外部方法存在事务,将挂起当前事务,服务2会以非事务方式执行,不会回滚
6.2.2 内部方法出现异常
@Transactional
@Override
public void required_not_notEx() {
    user1Service.addNotSupported(createUser1());
    user2Service.addNotSupportedThrow(createUser2());
}
服务结果分析
服务1成功外部方法存在事务,将挂起当前事务,服务1会以非事务方式执行,不会回滚
服务2成功外部方法存在事务,将挂起当前事务,服务1会以非事务方式执行,不会回滚
6.2.3 捕获内部方法的异常
@Transactional
@Override
public void required_not_notCatch() {
    user1Service.addNotSupported(createUser1());
    try {
        user2Service.addNotSupportedThrow(createUser2());
    } catch (Exception ignored) {}
}
服务结果分析
服务1成功外部方法存在事务,将挂起当前事务,服务1会以非事务方式执行,不会回滚
服务2成功外部方法存在事务,将挂起当前事务,服务1会以非事务方式执行,不会回滚

7 NEVER

以非事务方式执行;如果当前存在事务,就抛出异常。

新建如下方法,@Transactional注解的propagation参数设置为Propagation.NEVER

// 使用事务插入
@Transactional(propagation = Propagation.NEVER)
@Override
public void addNever(User1 user) {...}
// 使用事务插入
@Transactional(propagation = Propagation.NEVER)
@Override
public void addNever(User2 user) {...}

// 使用事务插入,抛出异常
@Transactional(propagation = Propagation.NEVER)
@Override
public void addNeverThrow(User2 user) {...}

7.1 场景1:外部方法没有开启事务

7.1.1 外部方法出现异常
@Override
public void noEx_never_never() {
    user1Service.addNever(createUser1());
    user2Service.addNever(createUser2());
    throw new RuntimeException();
}
服务结果分析
服务1成功外部方法没有事务,以非事务形式执行,不会回滚
服务2成功外部方法没有事务,以非事务形式执行,不会回滚
7.1.2 内部方法出现异常
@Override
public void no_never_neverEx() {
    user1Service.addNever(createUser1());
    user2Service.addNeverThrow(createUser2());
}
服务结果分析
服务1成功外部方法没有事务,以非事务形式执行,不会回滚
服务2成功外部方法没有事务,以非事务形式执行,不会回滚

7.2 场景2:外部方法开启事务

@Transactional
@Override
public void requiredEx_never_never() {
    user1Service.addNever(createUser1());
    user2Service.addNever(createUser2());
}
服务结果分析
服务1异常外部存在事务,直接抛出异常,不会执行
服务2异常外部存在事务,直接抛出异常,不会执行

此时抛出的异常为:org.springframework.transaction.IllegalTransactionStateException: Existing transaction found for transaction marked with propagation 'never'

附录

案例代码:点击查看源码