@Transactional 加了却没回滚,到底是哪里出了问题?哪些写法会让事务悄悄失效?这篇列出十几个场景,每个都在 Spring Framework 7.0.9 和 MySQL 26.7.0 上跑一遍,看表里是不是真的回滚。
(0)对照:public 方法,通过代理调用 → 调用者看到 IllegalStateException 表里剩 0 行 → 回滚了(事务生效) (1)自调用:未加注解的 outerNoTx() 里调 this.selfTx() → 调用者看到 IllegalStateException 表里剩 1 行 → 没回滚(事务没生效) (2)package-private 的 @Transactional 方法 → 调用者看到 IllegalStateException 表里剩 0 行 → 回滚了(事务生效) (3)protected 的 @Transactional 方法 → 调用者看到 IllegalStateException 表里剩 0 行 → 回滚了(事务生效) (4)private 的 @Transactional 方法(经 callPriv 调用) → 调用者看到 IllegalStateException 表里剩 1 行 → 没回滚(事务没生效) (5)final 的 @Transactional 方法 → 调用者看到 IllegalStateException 表里剩 1 行 → 没回滚(事务没生效) (6)方法里 catch 吞掉异常 → 调用者看到 正常返回 表里剩 1 行 → 没回滚(事务没生效) (7)在 @Transactional 里开线程做 insert,之后主线程抛异常 → 调用者看到 IllegalStateException 表里剩 1 行 → 没回滚(事务没生效) (8)事务管理器绑定的是 A 库,方法却写 B 库 → 调用者看到 IllegalStateException 表里剩 1 行 → 没回滚(事务没生效) (9)表是 MyISAM 引擎(不支持事务) → 调用者看到 IllegalStateException 表里剩 1 行 → 没回滚(事务没生效) (10)自己 new 出来的对象(不是 Spring 管理的) → 调用者看到 IllegalStateException 表里剩 1 行 → 没回滚(事务没生效) (11)注解写在类的方法上,经接口类型 Api 调用(CGLIB 模式) → 调用者看到 IllegalStateException 表里剩 0 行 → 回滚了(事务生效)
生效与不生效的场景逐个对照:不生效的八个各有原因,代理拦不到、事务不知道、事务管不到、没有事务能力。
JDK 代理(默认,Svc 实现了接口 Api):Bean 的类 $Proxy19;getBean(Svc.class) → NoSuchBeanDefinitionException 通过接口调用 viaInterface()(接口里声明了这个方法) → 调用者看到 IllegalStateException 表里剩 0 行 → 回滚了(事务生效)
默认的 JDK 代理只拦截接口方法且只能按接口取 Bean。