事务回滚失效主因是异常被吞没、未穿透事务边界,需确保未捕获异常触发回滚;Spring默认仅对RuntimeException和Error回滚,检查异常需显式配置rollbackFor;代理失效(如this调用、private/final方法)也会导致事务不生效,应通过Bean引用或编程式事务解决。

事务回滚失效,往往不是因为没配 @Transactional,而是异常被“悄悄吃掉”了——没抛出去,框架就收不到信号,自然不会回滚。关键在于让异常真实穿透事务边界。
别在事务方法里用 try-catch 吞掉异常
Spring 的事务回滚依赖于方法执行过程中**未被捕获的异常**。一旦你在加了 @Transactional 的方法内部用 try-catch 把异常 catch 住又没重新 throw,事务就默认成功提交。
- ❌ 错误写法:捕获后静默处理或只打日志
- ✅ 正确做法:catch 后必须 re-throw(可包装但不能吞)
- 示例:若需记录日志再回滚,应写成
throw new RuntimeException("业务失败", e);,保留原始异常堆栈
确保异常能被 Spring 识别为回滚触发点
Spring 默认只对 RuntimeException 和 Error 自动回滚。如果你抛的是 Exception(如 IOException、SQLException),它不会回滚,除非显式声明。
- 默认不回滚:
throw new Exception("文件读取失败"); - 强制回滚:加上
@Transactional(rollbackFor = Exception.class) - 更精准的做法:只对业务关心的检查异常设回滚,比如
rollbackFor = {IOException.class, BusinessException.class}
避开代理失效导致的“假事务”
事务是通过 Spring AOP 动态代理实现的,只有通过代理对象调用的方法才能被拦截。以下情况会让 @Transactional 形同虚设:
立即学习“Java免费学习笔记(深入)”;
- ❌ 在同一个类中用
this.methodB()调用另一个@Transactional方法 - ❌ 把事务方法写成
private或final(代理无法重写) - ✅ 正确方式:通过注入的 Bean 引用调用,或改用编程式事务(
TransactionTemplate)
验证回滚是否真生效
别靠“没报错”判断事务成功——要验证数据是否真的被撤销。
- 在事务方法末尾主动抛异常(如
throw new RuntimeException("test rollback")),观察数据库变更是否消失 - 开启
spring.jpa.show-sql=true并查看日志中是否有ROLLBACK记录 - 避免因读写分离导致“查不到刚写的值”,误以为回滚失败;查询时强制走主库确认状态


















