SpringBoot事务失效的6个坑,你踩过几个
发布时间:2026/9/30 9:40:26 作者:尧图编辑部 阅读量:1,286

在Spring Boot开发中Transactional注解看似简单却暗藏玄机。很多开发者以为加上注解就万事大吉结果生产环境出现数据不一致时才发现事务早已失效。事务失效也是面试中高频的追问点能准确说出几种失效场景往往能体现你对Spring AOP和事务机制的真正理解。下面这6个坑看看你踩过几个。坑1方法不是publicSpring事务基于AOP代理实现而代理只能拦截public方法。如果你把Transactional标在protected、private或包级方法上事务不会生效而且Spring通常不会报错只是静默忽略。示例java复制下载Transactional protected void updateStock() { ... } // 事务失效解决将方法改为public。如果确实需要非public可以考虑使用AspectJ静态织入但成本较高。坑2自调用导致代理失效同一个类中一个非事务方法直接调用另一个带Transactional的方法属于this调用不经过代理对象事务自然不生效。示例java复制下载public void createOrder() { this.updateInventory(); // 自调用事务失效 } Transactional public void updateInventory() { ... }解决注入自身代理、使用AopContext.currentProxy()或者将事务方法拆分到另一个Bean中。最推荐拆分职责清晰且无代理陷阱。坑3异常类型不匹配Spring默认只对RuntimeException和Error回滚。如果你抛出的是检查型异常如IOException、SQLException事务默认不回滚数据可能部分提交。示例java复制下载Transactional public void save() throws IOException { // 抛出IOException事务不回滚 }解决明确指定Transactional(rollbackFor Exception.class)覆盖所有异常。这是最稳妥的做法。坑4异常被捕获吞掉方法内部用try-catch捕获了异常却没有重新抛出Spring认为方法正常执行完毕于是提交事务。示例java复制下载Transactional public void transfer() { try { // 业务操作 } catch (Exception e) { log.error(失败, e); // 异常被吞事务提交 } }解决catch后重新抛出RuntimeException或者手动调用TransactionAspectSupport.currentTransactionStatus().setRollbackOnly()标记回滚。坑5多数据源未指定事务管理器当项目配置了多个数据源和多个PlatformTransactionManager时Transactional默认使用primary事务管理器。如果你操作的是另一个数据源事务不会生效。示例java复制下载Transactional // 默认用primary但操作的是second数据源 public void updateSecondDB() { ... }解决显式指定Transactional(transactionManager secondTransactionManager)确保与数据源匹配。坑6传播行为配置错误传播行为决定了事务如何嵌套。常见错误是在事务方法中调用标记为NOT_SUPPORTED的方法导致当前事务被挂起部分操作脱离事务或者误用REQUIRES_NEW使内外事务相互独立外层回滚不影响内层。示例java复制下载Transactional public void outer() { innerService.doWithNotSupported(); // 当前事务挂起操作无事务 }解决理解各传播行为语义按业务需求选择。默认REQUIRED适合大多数场景需要独立提交时用REQUIRES_NEW但要清楚其代价。总结事务失效的本质离不开三点代理机制、异常处理、配置错误。日常开发中建议养成以下习惯事务方法一律public避免自调用统一使用rollbackFor Exception.class不要吞异常多数据源显式指定事务管理器搞不清传播行为时保持默认。此外确保数据库表引擎为InnoDB否则再正确的注解也无济于事。面试中如果能结合AOP代理原理讲清这些坑你离offer就更近了一步。