@Transactional失效本质是未走AOP代理,典型场景有四:1.方法非public,源码中Modifier.isPublic校验失败;2.同类内部调用(this调用)绕过代理;3.异常被catch吞掉或非RuntimeException未配置rollbackFor;4.MySQL表引擎非InnoDB导致事务不支持。

Java事务失效不是代码报错,而是“静默失败”——数据写进去了,异常没回滚,业务逻辑看似跑通,实际已埋下数据不一致的隐患。核心就一条:Spring事务基于AOP代理,没走代理,事务就失效。下面直击最常踩的几个坑,不绕弯、不堆概念。
方法必须是 public 才生效
@Transactional 注解只对 public 方法起作用。private、protected、default(包级)方法加了也白加,Spring 在解析时会直接跳过。
- 源码佐证:AbstractFallbackTransactionAttributeSource 类中,!Modifier.isPublic(method.getModifiers()) 会返回 null,事务属性丢失
- 错误写法:
@Transactional private void save() { ... } - 正确写法:
@Transactional public void save() { ... }
同类内部调用不走代理
一个类里,A 方法调用本类的 B 方法(B 加了 @Transactional),用 this.b() 是直接调原对象,没经过 Spring 生成的代理对象,事务拦截不到。
- 典型场景:service 层拆分逻辑,外层方法没加事务,内层加了,但被 this 调用
- 解决方式有三种:
– 把方法移到另一个 service 类中(推荐,职责清晰)
– 注入自身 bean:@Autowired private UserService self;,然后用self.inner()
– 启用暴露代理:@EnableAspectJAutoProxy(exposeProxy = true),再用((UserService) AopContext.currentProxy()).inner()
异常被 catch 吞掉或类型不对
Spring 默认只对 RuntimeException 和 Error 自动回滚。检查型异常(比如 SQLException、IOException)不会触发回滚;更常见的是 try-catch 住异常却不抛出,事务完全感知不到出错了。
立即学习“Java免费学习笔记(深入)”;
- 默认不回滚:
throws Exception且未配置rollbackFor - 吞异常写法:
try { ... } catch (Exception e) { log.info("忽略"); }→ 事务认为一切正常 - 正确做法:
– 显式声明:@Transactional(rollbackFor = Exception.class)
– 或在 catch 里重新抛 RuntimeException:throw new RuntimeException(e)
– 或手动设回滚:TransactionAspectSupport.currentTransactionStatus().setRollbackOnly()
数据库引擎不支持事务
MySQL 中,如果表用的是 MyISAM 引擎,无论你怎么配 @Transactional,它都不支持事务——增删改不会回滚,连原子性都保证不了。
- 确认方式:
SHOW CREATE TABLE xxx;看 ENGINE=InnoDB - InnoDB 是唯一广泛支持事务的常用引擎;Memory、MyISAM 都不行
- 建表语句务必显式指定:
ENGINE=InnoDB DEFAULT CHARSET=utf8mb4


















