传播参数
假设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][2])相同,均为外层方法名; [3][5]抛异常,均会导致内外事务全部回滚;[4]抛异常,仅内层事务[4]回滚,外层事务[3][5]均正常写入。
事务与异步
事务内起新线程,新线程内的逻辑与当前事务互不影响。
可以从以下几个角度来理解这个结论:
- 从事务的ACID特性来看,不同事务之间需要满足隔离性。事务内起新线程,线程之间需要满足隔离性的要求。
- 从事务的实现来看,事务操作需要在同一个数据库连接
java.sql.Connection内完成。Spring会将java.sql.Connection对象保存在ThreadLocal里,从而实现“开启事务时当前线程总是获得同一个java.sql.Connection对象”,从而实现事务。对于不同的线程,自然无法保证拿到的是同一个java.sql.Connection对象。(可参考源码org.springframework.transaction.support.TransactionSynchronizationManager) - 事务注解文档(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.
不存在“多线程事务”,但可以考虑用分布式事务的方法来实现。
参考资料
- Spring事务传播规则——嵌套事务NESTED详解,https://www.cnblogs.com/yifanSJ/p/16330741.html
- Spring事务之如何保证同一个Connection对象,https://blog.csdn.net/oTengYue/article/details/51145990
- 要我说,多线程事务它必须就是个伪命题!,https://www.cnblogs.com/thisiswhy/p/13948055.html