在 Java 开发中,Spring 框架的声明式事务是最常用的事务管理方式,但在某些场景下,事务可能会失效。以下是常见的事务失效场景及解决方案:

1. 方法非 public 修饰

  • 问题:Spring AOP 要求事务方法必须是 public 的,否则事务失效。

  • 原因:Spring 通过代理模式实现事务,非 public 方法会被 AOP 忽略。

  • 示例

    // 事务失效
    private void doBusiness() {
        // 数据库操作
    }
    
  • 解决方案:将方法改为 public

2. 同类方法内部调用

  • 问题:同一个类中,非事务方法调用事务方法,事务失效。

  • 原因:内部调用绕过了 Spring 代理,事务注解无法生效。

  • 示例

    public class UserService {
        public void nonTransactionalMethod() {
            // 直接调用同类中的事务方法
            this.transactionalMethod(); // 事务失效
        }
    
        @Transactional
        public void transactionalMethod() {
            // 数据库操作
        }
    }
    
  • 解决方案

    • 通过ApplicationContext或@Autowired

      注入自身代理:

      @Service
      public class UserService {
          @Autowired
          private UserService self;
      
          public void nonTransactionalMethod() {
              self.transactionalMethod(); // 通过代理调用,事务生效
          }
      }
      

3. 异常类型不匹配

  • 问题:事务方法抛出的异常未被 @Transactional 注解的 rollbackFor 覆盖。

  • 原因:默认情况下,Spring 事务只对 RuntimeExceptionError 回滚,对受检异常(如 IOException)不回滚。

  • 示例

    @Transactional
    public void doBusiness() throws Exception {
        // 抛出受检异常,但未指定 rollbackFor
        throw new Exception("事务不回滚");
    }
    
  • 解决方案

    @Transactional(rollbackFor = Exception.class) // 明确指定回滚的异常类型
    public void doBusiness() throws Exception {
        // ...
    }
    

4. 事务传播行为配置错误

  • 问题:错误配置传播行为(如 Propagation.NOT_SUPPORTED)导致事务失效。

  • 示例

    @Transactional(propagation = Propagation.NOT_SUPPORTED) // 不支持事务
    public void doBusiness() {
        // 数据库操作,事务失效
    }
    
  • 解决方案:根据业务需求选择合适的传播行为(如 REQUIRED)。

5. 数据源未配置事务管理器

  • 问题:未显式配置 DataSourceTransactionManagerJpaTransactionManager

  • 解决方案

    确保配置类中声明了事务管理器:

    @Configuration
    public class TransactionConfig {
        @Bean
        public PlatformTransactionManager transactionManager(DataSource dataSource) {
            return new DataSourceTransactionManager(dataSource);
        }
    }
    

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

  • 问题:MySQL 表使用 MyISAM 引擎(不支持事务),即使配置了事务也无效。

  • 解决方案

    将表引擎改为 InnoDB

    ALTER TABLE your_table ENGINE=InnoDB;
    

7. 多线程场景

  • 问题:事务方法在新线程中执行,导致事务上下文丢失。

  • 示例

    @Transactional
    public void doBusiness() {
        new Thread(() -> {
            // 数据库操作,事务失效(新线程无事务上下文)
        }).start();
    }
    
  • 解决方案:避免在事务方法中开启新线程执行数据库操作。

8. 手动捕获异常未抛出

  • 问题:事务方法中捕获异常但未重新抛出,导致事务不回滚。

  • 示例

    @Transactional
    public void doBusiness() {
        try {
            // 数据库操作
        } catch (Exception e) {
            // 未抛出异常,事务提交
        }
    }
    
  • 解决方案

    @Transactional
    public void doBusiness() {
        try {
            // ...
        } catch (Exception e) {
          TransactionAspectSupport.currentTransactionStatus().setRollbackOnly(); // 手动回滚
        }
    }
    

总结

事务失效的核心原因是 Spring AOP 代理机制被绕过事务配置不符合预期。排查时需注意:

  1. 方法权限与调用方式。
  2. 异常类型与传播行为配置。
  3. 数据库引擎与事务管理器配置。
  4. 多线程与异常处理逻辑。

通过遵循 Spring 事务的使用规范,可以有效避免事务失效问题。

Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐