@Transactional 失效场景


@Transactional 失效是 Java 面试和实际开发中最最最经典的问题之一。虽然 Spring 帮我们把事务封装得很简单,但其实它非常“娇贵”,稍不注意就罢工。

我把失效场景分为 3 大类 8 小种,并按照“踩坑频率”排序,帮你一次性理清楚。

第一类:自身调用问题(最常见,踩坑王)

这是 AOP 的原理决定的,Spring 的事务是基于代理类(Proxy)实现的。

1. 同类内部自调用(Self-Invocation)

  • 场景:你在 Service 的一个非事务方法 A 里,调用了同一个类的事务方法 B。
@Service
public class OrderService {
    
    // 方法 A 没有注解
    public void createOrder() {
        // ...
        this.saveOrder(); // ❌ 这里的调用,事务会失效!
    }

    @Transactional
    public void saveOrder() {
        // 写数据库...
    }
}
  • 原因:this.saveOrder() 调用的是对象本身的方法,绕过了 Spring 的代理对象,Spring 根本不知道你调用了,所以没法开启事务。
  • 解决:把 saveOrder 放到另一个 Service 类里调用。
    或者注入自己:@Autowired OrderService self; self.saveOrder();

第二类:使用姿势不对(配置错误)

2. 方法修饰符不是 public

  • 场景:把 @Transactional 加在了 private 或 protected 方法上。
@Transactional
private void save() { // ❌ 失效
}
  • 原因:Spring 默认通过动态代理(CGLIB/JDK)实现事务,代理类无法拦截私有方法。

3. 方法是 final 的

  • 场景:方法被 final 修饰。
  • 原因:代理类无法重写 final 方法,无法植入事务逻辑。

4. 异常类型不匹配(经典坑)

  • 场景:你抛出了一个 Exception(Checked Exception),但没告诉 Spring。

@Transactional
public void save() throws Exception {
    // ...
    throw new Exception("出错啦"); // ❌ 事务不会回滚!
}
  • 原因:Spring 默认只在抛出 RuntimeException (运行时异常) 和 Error 时才回滚。普通的 Exception 它认为是业务逻辑,不回滚。
    解决:显式指定:@Transactional(rollbackFor = Exception.class)。(强烈建议所有地方都加上这个)

5. 异常被你自己“吃”掉了

  • 场景:你在事务方法里 try-catch 了异常,但没有再抛出来。
@Transactional
public void save() {
    try {
        userDao.insert(user);
        int i = 1 / 0; // 报错
    } catch (Exception e) {
        e.printStackTrace(); // ❌ 异常被捕获了,Spring 以为一切正常,提交事务
    }
}
  • 解决:在 catch 块里手动抛出:throw new RuntimeException(e);
    或者手动回滚:TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();

第三类:数据库/容器环境问题

6. 数据库引擎不支持事务

  • 场景:你用的是 MySQL,但表的引擎是 MyISAM。
  • 原因:MyISAM 根本不支持事务,Spring 再努力也没用。
  • 解决:改成 InnoDB 引擎。

7. 多线程调用

  • 场景:在事务方法里开启了新线程去写库。
@Transactional
public void main() {
    new Thread(() -> {
        userService.save(); // ❌ 这是新线程,不归主线程的事务管
    }).start();
}
  • 原因:Spring 的事务是和**线程(ThreadLocal)**绑定的。新线程有自己的连接,和主线程不是同一个事务。

8. 事务传播机制配置错误

  • 场景:你手动设置了 propagation = Propagation.NOT_SUPPORTED。
  • 原因:这配置的意思就是“我不支持事务,如果有事务就挂起”。你自己把它关了,当然失效。

总结(防坑指南)

如果你发现事务不回滚,按这个顺序排查:

  • 看日志:有没有报错?还是报错被 catch 了?
  • 看注解:是不是忘了加 rollbackFor = Exception.class?
  • 看调用:是不是用了 this.xxx() 内部调用?
  • 看权限:方法是不是 public?
  • 看表引擎:是不是 InnoDB?
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值