一、写在前面做后端开发这几年Spring 事务算是我踩坑最多的一个点。明明注解加了代码看着也没问题可数据就是没回滚。后来排查多了才发现事务失效往往不是框架的锅而是我们自己没注意细节。今天把常见的 8 种场景整理出来希望能帮你少走弯路。二、8 种事务失效场景1. 方法内部自调用这是最隐蔽的一种。同一个类里A 方法调用 B 方法B 方法上虽然标了Transactional但事务根本不会生效。因为 Spring 事务是基于代理实现的自调用走的是 this 调用绕过了代理对象。Service public class OrderService { public void createOrder() { // 这里调用的是 this.saveOrder()事务不生效 saveOrder(); } Transactional public void saveOrder() { // 数据库操作 } }解决办法很简单把事务方法拆到另一个 Bean 里或者注入自身代理。2. 方法不是 public 的Spring 默认只对 public 方法做事务代理。如果你把Transactional标在 private 或 protected 方法上事务同样不会生效。虽然 IDE 不会报错但运行起来就是静默失效。Service public class UserService { Transactional private void updateUser() { // 私有方法事务不生效 } }3. 异常被吞掉了事务回滚靠的是异常触发。如果你在方法里把异常 catch 住Spring 根本感知不到自然就不会回滚。这是新手最容易犯的错。Transactional public void transfer() { try { // 扣款 // 加款 } catch (Exception e) { // 异常被吞掉事务不会回滚 log.error(转账失败, e); } }正确做法是 catch 之后重新抛出或者干脆不 catch让异常往上抛。4. 抛出的是检查型异常Spring 默认只对 RuntimeException 回滚。如果你抛的是 IOException 这类检查型异常事务不会回滚。很多人在这里栽跟头。Transactional public void importData() throws IOException { // 抛 IOException默认不回滚 Files.copy(source, target); }解决办法是在注解里指定回滚异常Transactional(rollbackFor Exception.class)。5. 事务方法被 final 修饰final 方法无法被代理类重写Spring 的 CGLIB 代理拿它没办法事务自然失效。这个坑比较冷门但遇到了真的很头疼。Service public class PayService { Transactional public final void pay() { // final 方法事务不生效 } }6. 数据库引擎不支持事务这个坑属于环境问题。比如 MySQL 的 MyISAM 引擎就不支持事务你代码写得再对也没用。建表的时候一定要确认用的是 InnoDB。-- 建表时指定 InnoDB 引擎 CREATE TABLE order ( id BIGINT NOT NULL AUTO_INCREMENT, amount DECIMAL(10,2) DEFAULT NULL, PRIMARY KEY (id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4;7. 多线程环境下事务失效事务和线程是绑定的。你在主线程里开启事务然后在子线程里做数据库操作子线程的修改是不受主线程事务控制的。这个场景在异步任务里特别常见。Transactional public void process() { // 主线程事务 new Thread(() - { // 子线程操作数据库事务不生效 updateData(); }).start(); }8. 传播行为配置错误Spring 事务默认的传播行为是 REQUIRED也就是有事务就加入没有就新建。但如果你手动改成 NOT_SUPPORTED 或 NEVER事务就不会开启。这种问题一般出现在老代码里排查起来比较费劲。Transactional(propagation Propagation.NOT_SUPPORTED) public void noTxMethod() { // 明确不启用事务 }三、避坑清单总结最后把重点整理成一张清单方便你对照排查事务方法要放在独立的 Bean 里避免自调用。方法访问权限用 public别用 final 修饰。异常要么不 catch要么 catch 后重新抛出。记得配置rollbackFor Exception.class。确认数据库引擎是 InnoDB。多线程场景要单独处理事务边界。传播行为别乱改默认 REQUIRED 就够用。事务这东西原理其实不复杂难的是各种边界情况。希望这份清单能帮你少踩几个坑。如果你也遇到过其他奇葩的失效场景欢迎在评论区补充大家一起避坑。