Spring事务传播
本文详解Spring事务传播行为, 如下为@Transactional注解说明
讲事务传播之前, 需要先会用@Transactional注解
-
作用位置: 业务层接口上方,或者实现类上方,或者具体业务方法上方
-
作用: 为当前的业务方法添加事务支持
-
@Transactional注解属性
-
名称 作用 readOnly 表示只读,没有写操作。可以通过这个属性告诉数据库我们没有写操作,从而数据库可以针对只读sql做优化操作 timeout 事务再执行的时候,由于某些原因卡住,长时间占用数据库资源。此时很可能程序sql有问题,希望撤销事务,能够让事务结束,释放资源,即超时回滚 rollbackFor&rollbackForClassName 回滚策略,希望对于什么样的异常回滚, 注意并不是所有的异常 Spring 都会回滚,Spring 只对 Error 异常和 RuntimeException 异常回滚 noRollbackFor&noRollbackForClassName 属性值为某个异常, 代表出现这个异常不回滚 isolation 设置事务隔离级别 propagation 事务传播级别
-
正文开始
首先开始前, 先说明一下:
-
外围方法
外围方法可以理解为外层方法, 比如outerMethod为外围方法, innerMethod为内层方法
public void outerMethod(){ innerMethod(); }
-
内层方法
外围方法中调用的方法, 如上所述的innerMethod方法
事务传播行为propagation详解
传播属性 | 说明 |
---|---|
REQUIRED | 外围方法会开启新事务,内部方法会加入到外部方法的事务中(@Transactional注解默认为REQUIRED) |
SUPPORTS | 外围方法没有事务,则内部方法不执行事务 |
MANDATORY | 使用当前事务,如果当前没有事务就抛异常 |
REQUIRES_NEW | 新建事务,如果当前存在事务,把当前事务挂起。其实就是REQUIRES_NEW会开启新事务, 外围方法抛异常不会影响 |
NOT_SUPPORTED | 不支持事务 |
NEVER | 不支持事务,如果存在事务还会抛异常 |
NESTED | 如果当前存在事务,则在嵌套事务内执行,如果不存在,执行REQUIRED类似操作 |
本文主讲REQUIRED和REQUIRES_NEW的区别:
- REQUIRED属性加到外围方法上, 该外围方法中的内部方法的事务会加入到外围方法的事务, 也就是该外围方法中持久层操作, 与内部方法共用一个事务, 无论是内部方法抛出异常还是外围方法抛出异常, 事务都会回滚~
- REQUIRES_NEW属性加到内部方法且外围方法也加@Transactional且传播属性为默认(REQUIRED), 内部方法会将外围方法的事务挂起, 自己重新开启一个单独的新事务, 执行并提交之后, 再执行外围方法的事务
注意!!!: 如果想实现REQUIRES_NEW开启新事务, 请务必保证外围方法的connection和内部方法的connection必须不能是同一个connection, 否则REQUIRES_NEW将失效, 事务会统一被外围方法管理!
使用注解
-
requiredNewIn方法加注解@Transactional(propagation = Propagation.REQUIRED), requiredNewOut方法加@Transactional(propagation = Propagation.REQUIRES_NEW)
结果: requiredNewIn方法和requiredNewOut方法事务互不影响, 出现异常各自回滚互不影响
public void transferRequiredNew(Long srcId, Long deskId, long money) {
//接收转账钱
requiredNewIn(deskId, money);
//转账扣钱
requiredNewOut(srcId, money);
}
@Transactional(propagation = Propagation.REQUIRED)
public void requiredNewIn(Long deskId, long money){
//接收转账钱
employeeMapper.inAccount(deskId,money);
}
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void requiredNewOut(Long srcId, long money){
//转账扣钱
employeeMapper.outAccount(srcId,money);
}
-
外围方法加注解@Transactional(propagation = Propagation.REQUIRED), 两个内部方法都不加事务注解
结果: requiredIn方法和requiredOut方法事务会加入到外围方法transferRequired中, 不论是哪个内部方法或者外围方法出现异常, 所有方法皆会回滚
@Transactional(propagation = Propagation.REQUIRED)
public void transferRequired(Long srcId, Long deskId, long money) {
//接收转账钱
requiredIn(deskId, money);
//出现异常
System.out.println(1/0);
//转账扣钱
requiredOut(srcId, money);
}
public void requiredIn(Long deskId, long money){
//接收转账钱
employeeMapper.inAccount(deskId,money);
}
public void requiredOut(Long srcId, long money){
//转账扣钱
employeeMapper.outAccount(srcId,money);
}
-
外围方法加注解@Transactional(propagation = Propagation.REQUIRED), 内部方法一个加注解@Transactional(propagation = Propagation.REQUIRES_NEW), 另一个内部方法不加注解
结果: 如果外围方法和内部加事务注解的方法使用同一个connection, 虽然内部方法使用Propagation.REQUIRES_NEW会新创建一个事务, 但由于外围方法和内部方法使用同一个connection, 则最终事务会被外围方法的事务合并, 所以如果加@Transactional(propagation = Propagation.REQUIRES_NEW)抛出异常, 同样会使整个内外部方法事务全部回滚, 如需内部方法单独控制自己的事务, 需要内防方法与外围方法不使用同一个connection
@Transactional(propagation = Propagation.REQUIRED)
public void transferRequired(Long srcId, Long deskId, long money) {
//接收转账钱
requiredIn(deskId, money);
//出现异常
System.out.println(1/0);
//转账扣钱
requiredOut(srcId, money);
}
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void requiredIn(Long deskId, long money){
//接收转账钱
employeeMapper.inAccount(deskId,money);
}
public void requiredOut(Long srcId, long money){
//转账扣钱
employeeMapper.outAccount(srcId,money);
}