You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

多线程场景下抛出异常时Spring事务未回滚问题

事务不回滚问题分析与修复方案

核心问题原因

  1. 异步执行导致事务上下文断裂
    你的transfer方法通过taskExecutor.submit异步调用transferTxn,子线程无法继承主线程的事务上下文,导致transferTxn中的操作无法在同一个事务中执行。withdraw执行完成后会直接提交,后续deposit抛异常时,已经无法回滚之前的余额更新。

  2. 非public方法导致AOP事务代理失效
    transferTxn是protected方法,Spring AOP默认只对public方法生成事务代理,即使类上标注了@Transactional,该方法的事务注解也不会生效,内部的withdraw和deposit会各自在独立的事务(或无事务)中执行,无法保证原子性。

具体修复步骤

1. 调整方法可见性与事务注解

将transferTxn改为public方法,并显式添加事务注解,确保Spring能拦截并生成事务代理:

@Transactional(rollbackFor = Exception.class)
public Account transferTxn(long srcAccId, long toAccId, BigDecimal amount, boolean isMultithread) throws Exception {
    Account savedAcc = withdraw(srcAccId, amount, true, isMultithread);
    //Error thrown from this method
    deposit(toAccId, amount, true, isMultithread);
    
    addTxnH(savedAcc.getAccId(), toAccId, amount, TXN_T_T);

    return savedAcc;
}

2. 移除异步执行逻辑(保证原子性的关键)

转账操作需要严格的原子性,异步执行会破坏事务的连续性,因此直接在transfer中同步调用transferTxn:

public Account transfer(long srcAccId, long toAccId, BigDecimal amount, boolean isMultithread) throws Exception {
    return transferTxn(srcAccId, toAccId, amount, isMultithread);
}

如果业务必须使用异步,那么需要引入分布式事务框架来保证跨线程的事务一致性,但这会大幅增加系统复杂度,非必要场景不建议。

3. 验证事务代理有效性

确保服务类通过Spring容器注入使用,不要自行new实例,否则AOP代理不会生效,事务注解无法起作用。

4. 确认异常传递逻辑

检查deposit方法抛出的RuntimeException是否能完整传递到transferTxn方法外层,当前depositTxn的catch块会重新抛出异常,这部分逻辑是正确的,能触发事务回滚。

内容的提问来源于stack exchange,提问作者Rachel

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.23 08:59:57