`@Transactional` 注解生效的条件

最近在修复一个事务不生效的问题。虽然最终排查出的原因是动态数据源未配置TransactionManager,在此记录一下事务注解生效的条件:

文章目录

  • Spring `@Transactional` 注解生效条件
    • 必须满足的条件
      • 1. Bean 被 Spring 托管
      • 2. 必须通过代理对象调用
      • 3. 方法必须是 `public`
      • 4. 异常需触发回滚
    • 依赖环境与配置
      • 5. 数据库引擎支持事务
      • 6. 正确配置事务管理器
      • 7. 避免事务内手动提交
  • 踩坑与验证
    • 常见踩坑
      • 1:内部调用绕过代理
      • 2:异常被捕获未抛出
    • 验证事务是否生效


Spring @Transactional 注解生效条件

必须满足的条件

1. Bean 被 Spring 托管

  • 要求:类必须由 Spring 管理(如 @Service@Component)。
  • 陷阱new 出来的对象或非 Spring 代理的 Bean 无效。
  • 示例
    // ✅ 正确
    @Service
    public class UserService {
        @Transactional
        public void save() {}
    }
    
    // ❌ 错误:非 Spring 管理的类
    UserService userService = new UserService();
    userService.save(); // 事务无效
    

2. 必须通过代理对象调用

  • 要求:Spring 通过 AOP 代理实现事务,需从外部调用事务方法。
  • 陷阱:同类内方法直接调用(this.xxx())会绕过代理。
  • 解决方案
    • 注入自身代理(需开启 @EnableAspectJAutoProxy(exposeProxy = true)):
      @Service
      public class OrderService {
          @Autowired
          private OrderService selfProxy; // 注入代理对象
      
          public void createOrder() {
              selfProxy.pay(); // 通过代理调用,事务生效
          }
      
          @Transactional
          public void pay() {}
      }
      
    • 将方法拆分到另一个 Bean。

3. 方法必须是 public

  • 要求:默认仅拦截 public 方法(Spring AOP 限制)。
  • 例外:使用 AspectJ 模式时可支持非 public 方法(需配置 mode = AdviceMode.ASPECTJ)。

4. 异常需触发回滚

  • 默认:仅 RuntimeExceptionError 会回滚。
  • 自定义:通过 rollbackFor 指定其他异常:
    @Transactional(rollbackFor = {IOException.class, SQLException.class})
    

依赖环境与配置

5. 数据库引擎支持事务

  • 要求:数据库引擎需支持事务(如 MySQL 的 InnoDB)。
  • 检查:确认引擎支持事务。

6. 正确配置事务管理器

  • 单数据源:Spring Boot 自动配置 DataSourceTransactionManager
  • 多数据源:需显式指定 @Qualifier 或标记 @Primary提高优先级:
    @Bean
    @Primary
    public PlatformTransactionManager txManager(DataSource dataSource) {
        return new DataSourceTransactionManager(dataSource);
    }
    

7. 避免事务内手动提交

  • 要求:事务方法内不要手动调用 Connection.commit() 或 JPA flush()
  • 示例
    @Transactional
    public void update() {
        jpaRepository.save(user); // ✅ 由 Spring 管理事务
        // ❌ 错误:手动提交
        Session session = entityManager.unwrap(Session.class);
        session.flush(); // 可能提前提交部分操作
    }
    

踩坑与验证

常见踩坑

1:内部调用绕过代理

  • 现象this.method() 调用事务无效。
  • 解决:使用代理对象调用或拆分方法。

2:异常被捕获未抛出

  • 现象try-catch 吞没异常,事务未回滚。
  • 解决:重新抛出异常或手动回滚:
    @Transactional
    public void process() {
        try {
            jpaRepository.save(data);
        } catch (Exception e) {
        	//手动回滚
            TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
            //或者抛出 Runtime异常: throw new RuntimeException(e.getMessage());
        }
    }
    

验证事务是否生效

  1. 强制回滚测试:手动抛出 RuntimeException 观察数据是否回滚。

你可能感兴趣的:(java,java,数据库)