被嵌套事务的一次折磨,让我刻骨铭心
各位码友,大家好。最近项目上遇到了一个嵌套事务的现场问题,部门几个同事加班好几个钟头才把问题摆平。今天把问题梳理出来,抛开业务代码,和大家分享分享。
那我先抛出一个问题,大家可以思考1分钟试试:
我现在有一个业务方法A,添加了事务,然后在该业务方法内,有for循环,循环里面调用了另外一个方法B,B也添加了事务。如果在B循环执行的途中,有异常发生,会发生什么事情?
我当时最直觉的想法是这样的:事务A里面有事务B,事务B在循环里面执行途中,如果有异常发生,应该是只会回滚当前那次循环,而不会影响到事务A。和我有相同想法的码友恭喜你们,大错特错,完全是想当然。这个问题必须要有前提,那就是我们今天要分享的核心知识点,事务的传播机制!
一,回顾Java中的事务
事务在Java中主要用于确保数据库操作的原子性、一致性、隔离性和持久性(ACID特性)。以下是常见需要用到事务的场景:
1.必须使用事务的情况
多表/多记录更新操作
当需要更新多个表或同一表中的多条记录时,必须保证这些操作要么全部成功,要么全部失败
转账类业务逻辑
// 从一个账户扣款并向另一个账户加款
@Transactional
public void transferMoney(Account from, Account to, double amount) {
from.debit(amount); // 扣款
to.credit(amount); // 存款
}
订单处理流程
创建订单、扣减库存、支付处理等需要作为一个整体操作
批量数据处理
批量导入/导出数据时需要保证所有数据的一致性
2.推荐使用事务的情况
-
复杂的业务逻辑
即使只涉及单表操作,但业务逻辑复杂,可能需要回滚 -
需要数据一致性的操作
如更新配置信息、系统参数等关键数据 -
长时间运行的操作
需要保持数据一致性的长时间操作
3.Java中事务的实现方式
-
声明式事务(推荐)
@Transactional public void businessMethod() { // 业务代码 }
-
编程式事务
不给例子,因为几乎不用。
4.事务的注意事项
- 事务的粒度不宜过大,避免长时间占用数据库连接
- 注意事务的传播行为(Propagation)和隔离级别(Isolation)设置
- 非数据库操作(如文件IO、网络请求)不应放在事务中,因为它们无法回滚
- 事务是保证数据完整性的重要机制,在涉及数据修改且需要保证一致性的场景下应该考虑使用。
二,@Transactional中的propagation属性
- REQUIRED:如果当前存在事务,则加入该事务;否则新建一个事务(默认值)
- NESTED:如果当前存在事务,则在嵌套事务内执行;否则新建事务
- NESTED:如果当前存在事务,则在嵌套事务内执行;否则新建事务
- REQUIRES_NEW:新建事务,如果当前存在事务,则挂起当前事务
- NESTED:如果当前存在事务,则在嵌套事务内执行;否则新建事务
- NESTED:如果当前存在事务,则在嵌套事务内执行;否则新建事务
- NESTED:如果当前存在事务,则在嵌套事务内执行;否则新建事务
回到我们的问题,当时我们事务A,事务B都没有设置传播属性,所以当时嵌套事务的传播属性为默认值,即REQUIRED。那么当事务B在for循环中发生了异常会发生什么事情呢?
B方法会加入A方法的事务
任何一个B方法抛出异常,整个事务回滚(包括A方法中已经执行的所有B方法操作)
对各位码友你们没有听错,当嵌套事务的传播属性为REQUIRED时,以上该情况会全部回滚!
而非我一厢情愿地以为只会在嵌套事务里面回滚自己那部分,不会影响到外层的事务。
那么我们要达到A事务里面嵌套B事务,并且B事务在循环当中,如果B事务发生异常,只回滚B事务这样的目标。应该怎么做呢?
两个方案:
- 第一个方案:去掉A事务,打破嵌套事务的这个场景,就不存在事务传播这个事情。只认B事务,发生异常就只会回滚当前事务。
- 第二个方案:在B事务的事务注解上添加传播属性,REQUIRES_NEW
@Transactional(propagation = Propagation.REQUIRES_NEW)
pubic void updateSomething() {
// 业务方法
}
当添加了传播机制REQUIRES_NEW,就能满足我们的目标,具体为:
- 每次调用B方法都会新建一个独立事务
- 抛出异常的B方法会回滚自己的事务
- 已经成功执行的B方法事务会独立提交
- A方法的事务会因为捕获到异常而回滚(如果A方法没有捕获异常)
这样就能让:
- 成功的B方法操作会持久化
- 失败的B方法操作会回滚
- A方法会回滚(但不会影响已经提交的B方法事务)
团队最后被折磨几个小时之后,发现只需要改这一行代码,真的人无语到极致真的会笑~~~
一句话总结:嵌套事务时传播机制至关重要,你问我其他几个传播机制为什么不讲?上班好几年才因为这个紧急问题用到了这两个,其他的留给感兴趣的码友自己查阅。
好的,以上就是这次嵌套事务传播机制的卖惨,如果你觉得我们很惨,码友们动动小手指,一箭三连!助力我冲击中国码王!