Spring @Transactional 事务失效 8 大场景 + 原因 + 解决方案

3 阅读2分钟

@TransactionalSpring 声明式事务的核心注解,作用是自动管理事务的开启、提交、回滚,无需手动写事务代码,是开发中最常用的事务方案。

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) {
        // 异常被吞 → 不回滚
    }
}

解决方案

  1. 不要手动 catch,让异常往上抛
  2. 非要 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 管理的 DataSourceJdbcTemplateMyBatis

6. 多数据源 / 分布式操作,只开了本地事务

现象

  • 两个库操作,一个成功一个失败,不同时回滚

原因

  • @Transactional本地事务,只管一个数据源
  • 跨库、微服务、消息队列 → 不生效

解决方案

  • 分布式事务:Seata、TCC、可靠消息最终一致等

7. 引擎不支持事务(MyISAM)

现象

  • 怎么抛异常都不回滚

原因

  • MySQL 的 MyISAM 不支持事务
  • InnoDB 才支持

解决方案

  • 表引擎改成 InnoDB

8. 事务传播行为设置错误

现象

  • 事务不生效、或部分回滚

原因

  • 比如用了 SUPPORTSNOT_SUPPORTEDNEVER
  • 没有事务就不创建,导致失效
// 错误示例
@Transactional(propagation = Propagation.NOT_SUPPORTED)

解决方案

  • 业务方法默认用 REQUIRED 即可