对线面试官:列出 @Transactional 注解下,事务失效的七种场景

对线面试官:列出 @Transactional 注解下,事务失效的七种场景
最新回答
好多余

2023-08-28 10:42:10

在Spring框架中,@Transactional注解用于声明事务边界,但某些场景下可能导致事务失效。以下是七种常见的事务失效场景及详细分析:

1. 异常被捕获后未抛出
  • 原因:Spring默认仅在捕获到RuntimeException或Error时回滚事务。若异常被捕获且未重新抛出,事务管理器无法感知异常,导致不回滚。
  • 示例:@Transactionalpublic void deleteUser() { userMapper.deleteUserA(); try { int i = 1 / 0; // 触发异常 userMapper.deleteUserB(); } catch (Exception e) { e.printStackTrace(); // 异常被捕获,未抛出 }}
  • 解决:确保异常被抛出,或通过@Transactional(rollbackFor = Exception.class)指定回滚异常类型。
2. 抛出非运行时异常
  • 原因:默认情况下,@Transactional仅对RuntimeException和Error回滚。若抛出检查型异常(如IOException),需显式配置rollbackFor。
  • 示例:@Transactionalpublic void deleteUser() throws MyException { userMapper.deleteUserA(); try { int i = 1 / 0; userMapper.deleteUserB(); } catch (Exception e) { throw new MyException(); // 非RuntimeException,事务不回滚 }}
  • 解决:添加rollbackFor = Exception.class或抛出RuntimeException。
3. 方法内部直接调用
  • 原因:Spring通过动态代理实现事务,内部调用(如this.method())会绕过代理,导致事务失效。
  • 示例:public void deleteUser() { deleteUser2(); // 内部调用,@Transactional失效}@Transactionalpublic void deleteUser2() { userMapper.deleteUserA(); int i = 1 / 0; userMapper.deleteUserB();}
  • 解决:通过自注入(@Autowired private UserService self)调用代理对象,或拆分到另一个Bean中。
4. 新开启线程
  • 原因:Spring事务基于ThreadLocal绑定数据库连接,新线程无法继承主线程的事务上下文。
  • 示例:@Transactionalpublic void deleteUser() { userMapper.deleteUserA(); new Thread(() -> { userMapper.deleteUserB(); // 在新线程中执行,事务失效 }).start();}
  • 解决:避免在新线程中操作数据库,或使用@Async配合事务传播行为(需谨慎配置)。
5. 注解在private方法上
  • 原因:Spring动态代理基于接口或CGLIB,无法代理private方法(CGLIB可代理,但依赖子类化,可能失效)。
  • 示例:@Transactionalprivate void deleteUser() { // IDE警告:方法必须可被重写 userMapper.deleteUserA();}
  • 解决:将@Transactional注解在public或protected方法上。
6. 数据库引擎不支持事务
  • 原因:如MySQL的MyISAM引擎不支持事务,需使用InnoDB。
  • 解决:检查数据库表引擎,确保为事务型引擎(如InnoDB)。
7. 事务传播属性配置错误
  • 原因:错误的传播行为(如PROPAGATION_NOT_SUPPORTED)会挂起当前事务,导致方法以非事务方式执行。
  • 示例:@Transactional(propagation = Propagation.NOT_SUPPORTED)public void deleteUser() { userMapper.deleteUserA(); // 无事务上下文}
  • 解决:根据业务需求选择正确的传播行为(如默认的REQUIRED)。
总结

事务失效的核心原因通常与代理机制异常处理线程上下文配置错误相关。理解Spring事务的实现原理(AOP + ThreadLocal)是避免此类问题的关键。实际开发中,建议结合日志(如spring-boot-starter-actuator)监控事务行为,及时发现问题。