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), 内部方法会将外围方法的事务挂起, 自己重新开启一个单独的新事务, 执行并提交之后, 再执行外围方法的事务
举例实现
-
transactionTakesEffect方法加注解@Transactional(propagation = Propagation.REQUIRED),
out方法加@Transactional(propagation = Propagation.REQUIRES_NEW)
in方法加注解@Transactional(propagation = Propagation.REQUIRED)结果: transactionTakesEffect方法的事务不会影响out方法的事务, 也就是出现异常transactionTakesEffect出现异常in方法会回滚而out方法不会回滚
@Service("employeeService")
public class EmployeeServiceImpl implements IEmployeeService {
@Autowired
private EmployeeMapper employeeMapper;
@Autowired
private OtherService otherService;
@Override
@Transactional(rollbackFor = RuntimeException.class, propagation = Propagation.REQUIRED)
public void transactionTakesEffect(Long srcId, Long deskId, long money) {
/**转账扣钱
* 由于使用事务传播方式为REQUIRES_NEW, 所以是挂起transactionTakesEffect方法创建的事务,
* 自己新建一个事务, 所以transactionTakesEffect出现异常不会回滚扣钱的操作
*/
otherService.out(srcId,money);
/**接收转账收钱
* 由于使用事务传播方式为REQUIRES, 所以是加入transactionTakesEffect方法创建的事务,
* 所以transactionTakesEffect出现异常会回滚加钱的操作
*/
otherService.in(deskId,money);
//出现异常
System.out.println(1/0);
}
}
@Component
public class OtherService {
@Autowired
private EmployeeMapper employeeMapper;
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void out(Long srcId, long money){
//转账扣钱
employeeMapper.outAccount(srcId,money);
}
@Transactional(propagation = Propagation.REQUIRED)
public void in(Long srcId, long money){
//转账扣钱
employeeMapper.inAccount(srcId,money);
}
}
-
外围方法加注解@Transactional(propagation = Propagation.REQUIRED), 两个内部方法都不加事务注解
结果: outNo方法和inNo方法会加入到外围方法transferRequired的事务中, 不论是哪个内部方法或者外围方法出现异常, 所有方法皆会回滚
@Service("employeeService")
public class EmployeeServiceImpl implements IEmployeeService {
@Autowired
private EmployeeMapper employeeMapper;
@Autowired
private OtherService otherService;
@Override
@Transactional(rollbackFor = RuntimeException.class, propagation = Propagation.REQUIRED)
public void transactionTakesEffectAndTwoMethodInsert(Long srcId, Long deskId, long money) {
/**转账扣钱
*/
otherService.outNo(srcId,money);
/**接收转账收钱
*/
otherService.inNo(deskId,money);
//出现异常
System.out.println(1/0);
}
}
@Component
public class OtherService {
@Autowired
private EmployeeMapper employeeMapper;
public void outNo(Long srcId, long money){
//转账扣钱
employeeMapper.outAccount(srcId,money);
}
public void inNo(Long srcId, long money){
//转账扣钱
employeeMapper.inAccount(srcId,money);
}
}
举例总结
抛出问题
在 Spring 中,如果 a 方法没有事务,而 b 方法有事务,那么 a 调用 b 时,事务情况是怎样的?
解答问题
- 首先事务是基于aop的, 所以说如果a方法和b方法处于同一个类中, 则属于内部调用, 而内部调用aop是不生效的, 显然事务不会生效
- 如果b方法是注入进来的, 那么b方法事务会正常生效, 但是只能是b方法中发生异常时生效, 而在a方法发生异常的时候已经执行过b方法了, b方法事务也不会回滚
感谢观看, 有问题可以留言, 看到会回复, 如有不对之处请大佬指教!