@Transactional 是 Spring 声明式事务的核心注解,作用是自动管理事务的开启、提交、回滚,无需手动写事务代码,是开发中最常用的事务方案。
1. 方法不是 public(最常见低级错误)
现象
- 加了
@Transactional,但报错 / 异常不回滚
原因
- Spring 事务基于 AOP 动态代理,只拦截 public 方法
- private、protected、default 权限 → 不扫描、不代理
解决方案
- 方法必须改成 public
2. 异常被 try-catch 吃掉了(最坑)
现象
- 代码报错了,但数据没回滚
原因
- Spring 事务只有感知到异常抛出才回滚
- 你自己 catch 住了,Spring 以为正常执行完毕
// 错误示例
@Transactional
public void save() {
try {
updateA();
updateB(); // 抛异常
} catch (Exception e) {
// 异常被吞 → 不回滚
}
}
解决方案
- 不要手动 catch,让异常往上抛
- 非要 catch,就手动标记回滚:
catch (Exception e) {
TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
}
3. 同一个类内部方法调用(this.xxx ())
现象
- A 方法没事务,调用同类里 B 方法(带 @Transactional)
- B 方法异常,不回滚
原因
- this. 方法 () 调用的是原始对象,不是 Spring 代理对象
- 没走 AOP → 事务不生效
@Service
public class OrderService {
// 无事务
public void outer() {
this.inner(); // this 不是代理 → 事务失效
}
@Transactional
public void inner() {
// ...
}
}
解决方案
- 方法直接对外暴露调用,不要内部套调用
- 或注入自身代理:
@Autowired
private OrderService self;
public void outer() {
self.inner(); // 走代理 → 事务生效
}
4. 抛出的异常不是 RuntimeException / Error
现象
- 抛了 Exception,事务不回滚
原因
- Spring 默认只回滚运行时异常和 Error
- 受检异常(Exception、IOException 等)不回滚
解决方案
- 统一加上:
@Transactional(rollbackFor = Exception.class)
5. 使用了非 Spring 管理的数据源
现象
- 事务注解正常,但不生效
原因
- 你自己 new 了 Connection、自己写 JDBC
- 不在 Spring 事务管理范围内
解决方案
- 统一使用 Spring 管理的
DataSource、JdbcTemplate、MyBatis
6. 多数据源 / 分布式操作,只开了本地事务
现象
- 两个库操作,一个成功一个失败,不同时回滚
原因
@Transactional是本地事务,只管一个数据源- 跨库、微服务、消息队列 → 不生效
解决方案
- 分布式事务:Seata、TCC、可靠消息最终一致等
7. 引擎不支持事务(MyISAM)
现象
- 怎么抛异常都不回滚
原因
- MySQL 的 MyISAM 不支持事务
- InnoDB 才支持
解决方案
- 表引擎改成 InnoDB
8. 事务传播行为设置错误
现象
- 事务不生效、或部分回滚
原因
- 比如用了
SUPPORTS、NOT_SUPPORTED、NEVER - 没有事务就不创建,导致失效
// 错误示例
@Transactional(propagation = Propagation.NOT_SUPPORTED)
解决方案
- 业务方法默认用 REQUIRED 即可