spring事务不回滚的根本原因是事务上下文未建立、异常未抛出、代理未生效等技术问题,而非“流程控制滞后”;常见情形包括:①try-catch吞异常未重抛;②this自调用绕过代理;③传播行为错配(如not_supported);④受检异常未配置rollbackfor。

“流程控制滞后导致事务不回滚”这个说法本身存在概念混淆——Spring 的 @Transactional 并不依赖于“流程控制是否滞后”,它失效的根本原因从来不是执行顺序慢或逻辑延迟,而是事务上下文未被正确建立、异常未穿透、代理未生效等确定性技术问题。所谓“滞后”,往往是表象,背后是机制误用。
异常被吞掉或未抛出
这是最常见也最容易被当成“滞后”的情况:方法内发生错误,但被 try-catch 捕获后静默处理或仅打日志,没再 throw 出去。
- Spring 事务切面只在方法抛出异常时触发回滚判断,捕获后不重抛 = 事务认为“一切正常”
- 哪怕你 catch 里写了
log.error("出错了"),只要没throw e或throw new RuntimeException(e),事务就提交 - 验证方式:临时删掉 try-catch,直接让异常冒泡,看是否回滚
自调用绕过代理(this.xxx())
同一个类中,A 方法加了 @Transactional,B 方法调用 this.A() —— 看似流程走到了,实际根本没经过 Spring 代理,事务逻辑压根没启动。
- 这不是“滞后”,是调用路径错误:this 指向原始对象,不是代理对象
- 现象是:方法执行了、数据库写入了、也没报错,但异常发生后数据还在
- 解决办法有三:抽成独立 Service、注入自身 Bean、或用
AopContext.currentProxy()强转调用(需开启expose-proxy=true)
事务传播行为与嵌套逻辑错配
多个 @Transactional 方法嵌套调用时,若传播行为设为 SUPPORTS 或 NOT_SUPPORTED,子方法就可能运行在无事务上下文中,父方法回滚也带不动它。
- 比如父方法用了
REQUIRED,子方法却配了NOT_SUPPORTED,那子方法的操作就脱离事务管理 - 看起来像“流程走到后面才出问题”,其实是子操作从一开始就没进事务
- 统一使用默认
REQUIRED,除非有明确隔离需求;避免在非必要处覆盖传播属性
受检异常未显式声明 rollbackFor
抛出 IOException、SQLException 这类 checked 异常,默认不会触发回滚——Spring 只对 RuntimeException 和 Error 自动回滚。
- 不是“异常来得晚”,是 Spring 根本不认这种异常为回滚信号
- 解决:在注解中加上
@Transactional(rollbackFor = Exception.class) - 更稳妥的做法是自定义业务异常继承
RuntimeException,避免每次都要写 rollbackFor











