Spring 事务失效场景与原因分析
“@Transactional 怎么不生效?” 这个问题被问了无数遍。本文总结 8 个事务失效场景,每个都有代码复现和修正方案。
一、事务不生效的总根源
- 1.1 Spring 事务是 AOP 代理实现的
- 1.2 所有失效原因最终都归结为”代理没有生效”或”异常没有触发回滚”
二、场景 1:非 public 方法
- 2.1 原理——CGLIB 无法代理 private/protected 方法
- 2.2 错误示例与修正
三、场景 2:自调用问题
- 3.1 同类中方法 A 调用方法 B,B 的事务失效
- 3.2 解决方案——
AopContext.currentProxy()或拆分为不同 Bean
四、场景 3:异常被捕获未抛出
- 4.1
try-catch吃掉异常 - 4.2 正确的做法——catch 后
throw new RuntimeException(e) - 4.3 或在 catch 中
TransactionAspectSupport.currentTransactionStatus().setRollbackOnly()
五、场景 4:异常类型不匹配
- 5.1
@Transactional默认只回滚 RuntimeException 和 Error - 5.2 受检异常(Checked Exception)不回滚
- 5.3
@Transactional(rollbackFor = Exception.class)指定回滚范围
六、场景 5:数据库引擎不支持事务
- 6.1 MyISAM 不支持事务
- 6.2 确认数据库引擎为 InnoDB
七、场景 6:多线程环境
- 7.1 事务和线程绑定的——
TransactionSynchronizationManager使用 ThreadLocal - 7.2
@Async+@Transactional的组合陷阱 - 7.3 子线程中无法获取父线程的事务
八、场景 7:传播行为配置不当
- 7.1
REQUIRES_NEW——挂起当前事务,新开事务 - 7.2
NESTED——嵌套事务,Savepoint 回滚 - 7.3
NOT_SUPPORTED——非事务方式运行 - 7.4 传播行为的决策指南
九、场景 8:同一个 Service 类内部嵌套调用
- 8.1
REQUIRES_NEW在同一个 Bean 内失效 - 8.2 原因——同一个代理对象内调用不走代理
十、排查与总结
- 10.1 开启事务日志——
logging.level.org.springframework.transaction.interceptor=TRACE - 10.2 事务失效排查流程图
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来源 谜思录!