文章目录
- 前言
- 一、try/catch 里把异常“吃掉了,事务不会回滚
- 二、方法抛出 Checked Exception 且未配置 rollbackFor,事务不会回滚
- 三:@Transactional 在同类方法调用会失效
- 四:private 方法导致事务失效
- 总结
前言
Spring事务默认只对 RuntimeException 和 Error 回滚,对 Checked Exception 不回滚;如果方法抛出 Checked Exception 且未配置 rollbackFor,事务会被正常提交。
Spring事务只在“代理对象调用方法时”生效,类内部 self-invocation(自调用)会绕过代理。
一、try/catch 里把异常“吃掉了,事务不会回滚
例子:
@Transactional @GetMapping("/test1")public voidcreate(){try{orderMapper.insert(new order(1L,1L));// 模拟异常 int a=1/0;}catch(Exception e){System.out.println("异常被捕获了:"+ e.getMessage());}}解决:
1.直接不 catch
@Transactional public voidcreate(){orderMapper.insert(new order(1L,1L));int a=1/0;// 自动回滚}2.重新抛出异常
@Transactional public voidcreate(){try{orderMapper.insert(new order(1L,1L));int a=1/0;}catch(Exception e){System.out.println("记录日志");throw new RuntimeException(e);// ⭐关键:必须抛出去}}3.手动标记回滚
@Transactional public voidcreate(){try{orderMapper.insert(new order(1L,1L));int a=1/0;}catch(Exception e){TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();}}二、方法抛出 Checked Exception 且未配置 rollbackFor,事务不会回滚
例子:
@Transactional public void test()throws Exception{throw new Exception("编译时异常");}❗关键原因
Spring 的逻辑是:
“我只会对 RuntimeException 感兴趣”你的 Exception 是:
Checked Exception → Spring 默认忽略回滚解决:
方案1:强制所有异常都回滚(推荐)
@Transactional(rollbackFor=Exception.class)public void test()throws Exception{throw new Exception("库存不足");}方案2:统一用 RuntimeException
@Transactional public voidtest(){throw new RuntimeException("库存不足");}三:@Transactional 在同类方法调用会失效
例子:
@Service public class OrderService{public voidcreateOrder(){insertOrder();// ❌ 同类调用}@Transactional public voidinsertOrder(){orderMapper.insert(...);int a=1/0;// 期待回滚}}实际发生什么?
createOrder()→ 直接调用 insertOrder()❌ 没经过 Spring 代理 ❌ @Transactional 不生效Spring事务基于AOP代理实现,仅在通过代理对象调用方法时生效;同类内部方法调用属于 self-invocation,不会经过代理,因此事务不会生效。
正确解决方案(重点)
✔ 方案1:拆服务(最推荐)
@Service public class OrderService{@Autowired private OrderTxService orderTxService;public voidcreateOrder(){orderTxService.insertOrder();}}@Service public class OrderTxService{@Transactional public voidinsertOrder(){orderMapper.insert(...);}}方案2:通过代理调用自己(不推荐但可用)
@Autowired private ApplicationContext context;public voidcreateOrder(){OrderService proxy=context.getBean(OrderService.class);proxy.insertOrder();// ✔ 走代理}四:private 方法导致事务失效
@Transactional private voidb(){}❗原因
Spring AOP 无法代理 private 方法 只能代理 public/protected总结
Spring 的声明式事务基于 AOP 实现。@Transactional 只是事务声明,Spring 会通过 JDK 动态代理或 CGLIB 为目标 Bean 创建代理对象。方法调用时先进入事务拦截器,由事务拦截器负责开启事务、提交事务和回滚事务。因此事务能否生效的关键在于是否经过代理对象,这也是同类调用、private 方法、final 方法等场景事务失效的根本原因。
面试生产环境中事务怎么使用?
1.统一配置 rollbackFor=Exception.class2.非核心业务拆解,用异步保证一致性3.统一异常体系:继承RuntimeException重写异常类4.事前压测@作者:加辣椒了吗?
简介:憨批大学生一枚,喜欢在博客上记录自己的学习心得,也希望能够帮助到你们!
我在「辣椒微创」有更详细的案例和工具,你应该用得上!(主页有指路)
🚀 【创业项目】:副业灵感、轻资产玩法、互联网创收思路
📦 【资源分享】:PPT、音效、模板、字体、插件等创作者工具合集
🔧 【软件下载】:精选高效、靠谱的生产力工具及安装指南
💻 【编程开发】:编程学习资源、项目源码、开发工具、实战案例
🛒 【薅羊毛】:精选各种优惠折扣、平台补贴信息,帮你省钱放大收益。