Java 里我们说的事务,默认都是 Spring 声明式事务(@Transactional),它是基于 AOP 动态代理 实现的,这是事务失效的根本原因 ——没有走 Spring 代理对象,而是直接调用了原生对象方法。
我把最常见、面试必问、开发必踩的坑整理好了,直接看场景 + 原因 + 解决方案:
1. 方法不是 public(最基础的失效)
失效代码
java
运行
@Transactional
private void addUser() { // private/protected/default 都不行
userMapper.insert();
}
原因
Spring 事务只对 public 方法生效,AOP 代理无法拦截非 public 方法。
解决
方法改成 public。
2. 同一个类内部调用(90% 的人踩过坑)
失效代码
java
运行
@Service
public class UserService {
public void outer() {
// 直接调用同类方法 → 没走代理 → 事务失效!
this.inner();
}
@Transactional
public void inner() {
userMapper.insert();
}
}
原因
this.方法() 是原生对象调用,不是 Spring 代理对象调用,AOP 无法拦截。
解决
- 注入自身代理对象
- 把方法拆到不同类
- 使用 AopContext.currentProxy ()
3. 异常被 try-catch 吃掉了
失效代码
java
运行
@Transactional
public void add() {
try {
userMapper.insert();
int i = 1 / 0; // 抛异常
} catch (Exception e) {
// 异常被捕获 → 事务不知道要回滚
}
}
原因
Spring 事务只有感知到未捕获的异常才会回滚。
解决
不要吞异常,要么抛出去,要么手动回滚:
java
运行
TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
4. 抛出的异常类型不对
失效代码
java
运行
@Transactional // 默认只回滚 RuntimeException 和 Error
public void add() throws Exception {
userMapper.insert();
throw new Exception(); // 受检异常 → 不回滚!
}
原因
@Transactional 默认只对 RuntimeException/Error 回滚。
解决
指定回滚异常:
java
运行
@Transactional(rollbackFor = Exception.class)
5. 数据库引擎不支持事务
失效场景
MySQL MyISAM 引擎不支持事务,无论怎么写都无效。
解决
把表引擎改成 InnoDB。
6. 没有被 Spring 管理(类没加 @Service/@Component)
失效代码
java
运行
// 没加 @Service → 不是 Spring bean → 事务无效
public class UserService {
@Transactional
public void add() {}
}
原因
Spring 只管理自己容器里的 Bean。
解决
类上加 @Service/@Component。
7. 多线程调用(子线程事务失效)
失效代码
java
运行
@Transactional
public void outer() {
new Thread(() -> {
// 子线程独立连接 → 事务不生效
userMapper.insert();
}).start();
}
原因
Spring 事务是线程绑定的,子线程拿不到主线程的连接。
解决
子线程方法单独加 @Transactional,或不用异步。
8. 传播机制配置错误
失效代码
java
运行
@Transactional(propagation = Propagation.NOT_SUPPORTED)
原因
NOT_SUPPORTED / NEVER 这些传播机制本身就不开启事务。
解决
使用默认的 REQUIRED。
一句话总结根本原因
Spring 事务 = 动态代理 + public 方法 + 异常抛出 + 代理调用只要破坏任意一条,事务就可能失效。
总结
- 最常见失效:同类内部调用(this. 方法)、异常被 catch、方法非 public
- 根本原因:没走 Spring 代理对象
- 通用避坑:方法 public、异常不吞、同类调用用代理、rollbackFor=Exception.class
Spring 事务避坑清单
一、事务生效前置条件(缺一必失效)
- 类必须被 Spring 管理:添加
@Service/@Component/@Repository - 加
@Transactional的方法必须是 public - 调用必须通过 Spring 代理对象,不能用
this内部自调用 - 数据库表引擎为 InnoDB(MyISAM 不支持事务)
- 保证数据源、事务管理器已正常配置(SpringBoot 自动配置一般无需手动处理)
二、高频失效场景 + 标准正确写法
场景 1:同类内部调用(最高发)
❌ 错误(this 调用,不走代理,事务失效)
@Service
public class OrderService {
public void createOrder() {
this.saveOrder(); // 原生对象调用,AOP无法拦截
}
@Transactional(rollbackFor = Exception.class)
public void saveOrder(){
// 业务CRUD
}
}
✅ 方案 1:暴露自身代理(推荐)
// 开启代理暴露
@EnableAspectJAutoProxy(exposeProxy = true)
public void createOrder() {
OrderService proxy = (OrderService) AopContext.currentProxy();
proxy.saveOrder(); // 走代理,事务生效
}
✅ 方案 2:拆分到不同 Service(最稳妥)
场景 2:try-catch 捕获异常,事务不回滚
❌ 错误(吃掉异常,Spring 感知不到)
@Transactional(rollbackFor = Exception.class)
public void pay() {
try {
// 数据库操作
int a = 1 / 0;
} catch (Exception e) {
log.error("异常",e);
}
}
✅ 正确 1:捕获后手动回滚
@Transactional(rollbackFor = Exception.class)
public void pay() {
try {
// 数据库操作
int a = 1 / 0;
} catch (Exception e) {
// 手动标记回滚
TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
log.error("异常已回滚",e);
}
}
✅ 正确 2:不捕获异常,直接向上抛出
场景 3:受检异常不回滚(默认只回滚运行时异常)
❌ 错误(抛出 Exception/IOException 等,不会回滚)
@Transactional
public void biz() throws Exception {
throw new Exception("业务失败");
}
✅ 标准通用写法(全局全覆盖)
@Transactional(rollbackFor = Exception.class)
public void biz() throws Exception {
throw new Exception("业务失败");
}
场景 4:传播行为配置错误导致无事务
❌ 以下配置天然关闭事务 / 禁止事务
// 不使用事务
@Transactional(propagation = Propagation.NOT_SUPPORTED)
// 强制非事务,有事务直接报错
@Transactional(propagation = Propagation.NEVER)
✅ 常规业务统一使用默认
// propagation = Propagation.REQUIRED (默认)
@Transactional(rollbackFor = Exception.class)
场景 5:子线程异步,事务隔离失效
❌ 错误(主线程事务无法传递到子线程)
@Transactional(rollbackFor = Exception.class)
public void mainBiz(){
new Thread(()->{
// 独立事务/无事务,互不影响
mapper.insert();
}).start();
}
✅ 解决:子线程单独加事务或统一同步执行
场景 6:非 public 方法加事务
❌ 错误(private/protected/ 包级私有全部无效)
@Transactional(rollbackFor = Exception.class)
private void updateData(){
}
✅ 强制改为 public
三、生产项目事务注解【统一模板】
直接复制全局复用,杜绝 99% 事务问题
import org.springframework.transaction.annotation.Transactional;
/**
* 业务层统一事务注解
* 所有新增/修改/删除 方法必加
*/
@Transactional(rollbackFor = Exception.class)
public void businessMethod() {
// 增删改业务代码
}
四、快速排查事务失效 5 步口诀
- 看方法是不是 public
- 看是不是 this. 内部调用
- 看异常有没有被 全量 catch 吞掉
- 看有没有加 rollbackFor = Exception.class
- 检查表引擎是否为 InnoDB
五、补充:手动事务(无注解硬编码)
特殊场景不用注解,手动控制提交 / 回滚
@Autowired
private PlatformTransactionManager transactionManager;
public void manualTx(){
TransactionStatus status = transactionManager.getTransaction(new DefaultTransactionDefinition());
try {
// 业务CRUD
transactionManager.commit(status);
} catch (Exception e) {
transactionManager.rollback(status);
}
}
&spm=1001.2101.3001.5002&articleId=160607065&d=1&t=3&u=1d11a878f65a4e69a788e38688b107a7)
1232

被折叠的 条评论
为什么被折叠?



