Spring Boot事务管理失效?6种典型场景排查指南(附修复代码)

74 阅读1分钟

问题现象

最近在开发电商订单模块时,遇到一个诡异现象:

@Transactional
public void createOrder(OrderDTO dto) {
    // 1.保存订单主表
    orderMapper.insert(dto); 
    
    // 2.扣减库存(抛出运行时异常)
    inventoryService.deductStock(dto.getSkuId());
    
    // 3.记录操作日志(即使事务回滚,日志依然入库)
    logService.addLog("生成订单"); 
}

预期效果:当库存不足时,订单数据和日志记录应同时回滚
实际结果:订单数据回滚,但操作日志表却成功写入!


排查过程

1. 检查基础配置

✅ 确认启动类已添加@EnableTransactionManagement
✅ 检查数据库引擎为InnoDB(MyISAM不支持事务)

2. 代理机制验证

通过Debug查看Service实例类名:

System.out.println(this.getClass().getName());
// 输出结果:OrderServiceImpl$$EnhancerBySpringCGLIB$$...

结论:CGLIB代理生效(若为JDK动态代理需检查接口实现)


六大失效场景及修复方案

场景1:非public方法使用@Transactional

// ❌ 错误示例
@Transactional
private void saveOrder(Order order) {
    //...
}

原理:Spring AOP无法代理私有方法
修复:改为public访问权限


场景2:异常类型不匹配

@Transactional
public void createOrder() throws Exception {
    //...
    throw new SQLException(); // 检查异常
}

原理:默认只回滚RuntimeException和Error
修复

@Transactional(rollbackFor = Exception.class)

场景3:同一类内方法调用

public void processOrder() {
    this.createOrder(); // 内部调用不走代理
}

@Transactional
public void createOrder() {...}

原理:自调用绕过AOP代理
修复方案

  1. 注入自身代理对象
@Autowired 
private OrderService selfProxy;

public void processOrder() {
    selfProxy.createOrder(); 
}
  1. 使用AopContext(需开启exposeProxy)
((OrderService)AopContext.currentProxy()).createOrder();

场景4:多线程上下文丢失

@Transactional
public void asyncProcess() {
    new Thread(() -> {
        orderMapper.insert(...); // ❌ 新线程脱离事务控制
    }).start();
}

修复:使用TransactionTemplate手动管理

@Autowired