传播参数

假设A和B两个方法均有事务注解@Transactional,现在A调用B,B应该怎样响应A的事务?事务注解的propagation参数定义了这种情况下B的行为。

传播参数枚举:

传播参数 如果当前有事务 如果当前没事务
REQUIRED 加入当前事务 创建新事务
SUPPORTS 加入当前事务 以非事务方式执行
MANDATORY 加入当前事务 抛异常
REQUIRES_NEW 挂起当前事务,创建新事务 创建新事务
NOT_SUPPORTED 挂起当前事务,以非事务方式执行 以非事务方式执行
NEVER 抛异常 以非事务方式执行
NESTED 在嵌套事务中执行 创建新事务

默认REQUIRED。

嵌套事务

  • 主事务与嵌套事务属于同一事务
  • 嵌套事务抛异常,不会影响主事务
  • 主事务抛异常,嵌套事务也会回滚

测试代码:
内层

public class DepositHelperService {
    ......
    @Transactional(propagation = Propagation.NESTED)
    public void nestedInner(long id, int amount) {
        log.info("inner transaction name: {}", TransactionSynchronizationManager.getCurrentTransactionName());  // [1]
        atomicBillingService.insertOne(id, amount);
        if (100 == amount) {
            throw new RuntimeException("inner transaction exception");
        }
    }
}

外层

public class DepositService {
    ......
    @Transactional
    public void nestedOuter(long id, int amount) {
        log.info("outer transaction name: {}", TransactionSynchronizationManager.getCurrentTransactionName());  // [2]
        depositHelperService.nestedInner(id + 1, amount + 1);  // [3]
        try {
            depositHelperService.nestedInner(id + 2, amount + 2);  // [4]
        } catch (RuntimeException e) {
            log.error("inner exception", e);
        }
        atomicBillingService.insertOne(id, amount);
        if (100 == amount) {
            throw new RuntimeException("outer transaction exception");  // [5]
        }
    }
}

测试结果:

  1. 内外事务打印的事务名称([1][2])相同,均为外层方法名;
  2. [3][5]抛异常,均会导致内外事务全部回滚;
  3. [4]抛异常,仅内层事务[4]回滚,外层事务[3][5]均正常写入。

事务与异步

事务内起新线程,新线程内的逻辑与当前事务互不影响。

可以从以下几个角度来理解这个结论:

  1. 从事务的ACID特性来看,不同事务之间需要满足隔离性。事务内起新线程,线程之间需要满足隔离性的要求。
  2. 从事务的实现来看,事务操作需要在同一个数据库连接java.sql.Connection内完成。Spring会将java.sql.Connection对象保存在ThreadLocal里,从而实现“开启事务时当前线程总是获得同一个java.sql.Connection对象”,从而实现事务。对于不同的线程,自然无法保证拿到的是同一个java.sql.Connection对象。(可参考源码org.springframework.transaction.support.TransactionSynchronizationManager
  3. 事务注解文档(org.springframework.transaction.annotation.Transactional)指出:

    This annotation commonly works with thread-bound transactions managed by a org.springframework.transaction.PlatformTransactionManager, exposing a transaction to all data access operations within the current execution thread. Note: This does NOT propagate to newly started threads within the method.

不存在“多线程事务”,但可以考虑用分布式事务的方法来实现。

参考资料

  1. Spring事务传播规则——嵌套事务NESTED详解,https://www.cnblogs.com/yifanSJ/p/16330741.html
  2. Spring事务之如何保证同一个Connection对象,https://blog.csdn.net/oTengYue/article/details/51145990
  3. 要我说,多线程事务它必须就是个伪命题!,https://www.cnblogs.com/thisiswhy/p/13948055.html