【Java实战】被嵌套事务的一次折磨,让我刻骨铭心


各位码友,大家好。最近项目上遇到了一个嵌套事务的现场问题,部门几个同事加班好几个钟头才把问题摆平。今天把问题梳理出来,抛开业务代码,和大家分享分享。
那我先抛出一个问题,大家可以思考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.推荐使用事务的情况

  1. 复杂的业务逻辑
    即使只涉及单表操作,但业务逻辑复杂,可能需要回滚

  2. 需要数据一致性的操作
    如更新配置信息、系统参数等关键数据

  3. 长时间运行的操作
    需要保持数据一致性的长时间操作

3.Java中事务的实现方式

  1. 声明式事务(推荐)

    @Transactional
    public void businessMethod() {
        // 业务代码
    }
    
  2. 编程式事务
    不给例子,因为几乎不用。

4.事务的注意事项

  • 事务的粒度不宜过大,避免长时间占用数据库连接
  • 注意事务的传播行为(Propagation)和隔离级别(Isolation)设置
  • 非数据库操作(如文件IO、网络请求)不应放在事务中,因为它们无法回滚
  • 事务是保证数据完整性的重要机制,在涉及数据修改且需要保证一致性的场景下应该考虑使用。

二,@Transactional中的propagation属性

  1. REQUIRED:如果当前存在事务,则加入该事务;否则新建一个事务(默认值)
  2. NESTED:如果当前存在事务,则在嵌套事务内执行;否则新建事务
  3. NESTED:如果当前存在事务,则在嵌套事务内执行;否则新建事务
  4. REQUIRES_NEW:新建事务,如果当前存在事务,则挂起当前事务
  5. NESTED:如果当前存在事务,则在嵌套事务内执行;否则新建事务
  6. NESTED:如果当前存在事务,则在嵌套事务内执行;否则新建事务
  7. 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方法事务)

团队最后被折磨几个小时之后,发现只需要改这一行代码,真的人无语到极致真的会笑~~~

一句话总结:嵌套事务时传播机制至关重要,你问我其他几个传播机制为什么不讲?上班好几年才因为这个紧急问题用到了这两个,其他的留给感兴趣的码友自己查阅。

好的,以上就是这次嵌套事务传播机制的卖惨,如果你觉得我们很惨,码友们动动小手指,一箭三连!助力我冲击中国码王!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

wpw5499

若有帮助,不妨请博主一杯奶茶吧

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值