事务未回滚的根本原因是AOP代理失效:同类中用this调用导致@Transactional方法未被代理对象执行,事务从未开启。验证方式是检查this.getClass()是否为代理类名。

因为调用没走 Spring 的代理对象,事务切面根本没被触发。
根本原因:AOP 代理失效
Spring 的 @Transactional 是基于动态代理(JDK 或 CGLIB)实现的。只有通过代理对象调用的方法,才会被事务拦截器(TransactionInterceptor)拦截,在方法前后开启、提交或回滚事务。
但在同一个类中写 this.methodB(),调用的是目标对象本身,不是代理对象 —— 相当于绕过了整个 AOP 链路,@Transactional 注解形同虚设。
为什么异常抛出了,数据还是没回滚?
事务不回滚,不是因为没抛异常,而是因为事务压根没开启。典型表现:
- 日志里看不到
Creating new transaction或Initiating transaction rollback - SQL 执行了,但没被包裹在事务中(比如 MySQL 的 autocommit=1 模式下直接提交)
- 即使 methodB 抛 RuntimeException,methodA 也感知不到“这是个事务内失败”,因为它根本不在事务上下文中
常见误解澄清
有人以为“只要加了 @Transactional,方法里出错就一定回滚”——这不成立。前提是该方法必须被 Spring 代理接管。以下情况都会导致代理失效:
- 用
this.xxx()在同类中调用 - @Transactional 标在 private / final / package-private 方法上(代理无法增强)
- 方法被 try-catch 吞掉异常且未 re-throw
- 抛出的是 checked exception,但没在 @Transactional 中声明
rollbackFor
怎么验证是不是这个问题?
加一行日志或断点看调用栈:
- 如果调用链是
OrderService.add → OrderService.save,且 save 上有 @Transactional —— 大概率失效 - 如果调用链是
OrderService.add → OrderServiceProxy.save → save 实际逻辑—— 说明走的是代理,事务正常
也可以在 save 方法开头打印 this.getClass().getName():如果是 OrderService$$EnhancerBySpringCGLIB$$xxx,说明是代理类;如果是 OrderService,就是原始类,事务肯定没生效。

















