Skip to content

事务什么场景下会失效

约 803 字大约 3 分钟

MySQL美团

2025-04-09

⭐ 题目日期:

美团 - 2025/4/4

📝 题解:

事务失效的常见场景通常与事务管理配置、代码设计或运行时环境有关,以下是具体场景及原因分析:


1. 未启用事务管理

  • 场景:未在 Spring 配置中启用事务管理(如忘记 @EnableTransactionManagement)。
  • 解决:确保配置类或 XML 中启用了事务管理。

2. 数据库/存储引擎不支持事务

  • 场景:使用不支持事务的存储引擎(如 MySQL 的 MyISAM)。
  • 解决:切换为支持事务的引擎(如 InnoDB)。

3. 异常未被正确抛出

  • 场景
    • 默认情况下,Spring 事务仅在抛出 非检查型异常(如 RuntimeException)时回滚。
    • 若方法捕获异常未重新抛出,或仅抛出检查型异常(如 Exception),事务不会回滚。
  • 示例
    @Transactional
    public void method() {
        try {
            // 数据库操作
        } catch (Exception e) {
            // 捕获异常未抛出 → 事务不回滚
        }
    }
  • 解决
    • 使用 @Transactional(rollbackFor = Exception.class) 指定回滚的异常类型。
    • catch 中抛出 RuntimeException 或调用 TransactionAspectSupport.currentTransactionStatus().setRollbackOnly()

4. 非 Public 方法

  • 场景@Transactional 注解标注在非 public 方法上。
  • 原因:Spring AOP 无法代理非 public 方法。
  • 解决:将方法改为 public

5. 自调用问题

  • 场景:同一类中的方法直接调用 @Transactional 方法。
    public class ServiceA {
        public void methodA() {
            methodB(); // 直接调用 → 事务失效
        }
        @Transactional
        public void methodB() { /* 操作 */ }
    }
  • 原因:自调用绕过代理对象,事务拦截器未生效。
  • 解决
    • 通过注入自身代理对象调用(需启用 @EnableAspectJAutoProxy(exposeProxy = true))。
    • 将方法拆分到不同类中。

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

  • 场景:传播行为配置不当(如 REQUIRES_NEW 未生效)。
  • 示例
    @Transactional(propagation = Propagation.REQUIRED)
    public void methodA() {
        methodB(); // 预期开启新事务,但因配置错误未生效
    }
    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public void methodB() { /* 操作 */ }
  • 解决:检查传播行为配置,确保方法调用通过代理对象。

7. 多数据源未指定事务管理器

  • 场景:项目中使用多个数据源,但未明确指定事务管理器。
  • 解决:在 @Transactional 中指定 transactionManager 属性:
    @Transactional(value = "customTransactionManager")

8. 事务超时或只读设置冲突

  • 场景
    • @Transactional(timeout = 1) 设置过短,导致事务超时回滚。
    • @Transactional(readOnly = true) 下执行写操作。
  • 解决:调整超时时间或移除 readOnly 限制。

9. 异步方法未启用事务

  • 场景:在 @Async 方法中使用事务,未配置事务传播。
  • 解决:确保异步任务使用支持事务的线程池(如 TransactionalTaskExecutor)。

10. 手动提交干扰

  • 场景:在代码中手动调用 commit()rollback()(如直接使用 DataSourceTransactionManager)。
  • 解决:避免混合使用声明式事务和手动事务控制。

排查步骤

  1. 检查 @Transactional 注解是否生效(如方法是否为 public)。
  2. 确认事务管理器已正确配置。
  3. 检查异常是否按预期抛出。
  4. 使用调试工具(如 TransactionSynchronizationManager.isActualTransactionActive())验证事务是否激活。
  5. 查看日志中的事务生命周期(开启、提交、回滚)。

通过以上场景分析,可以快速定位事务失效的根本原因。