当前位置: 首页 > news >正文

Spring 事务失效场景详解

@Transactional看起来简单,但事务失效是项目里最常见的坑之一。要判断事务为什么没回滚,先抓住一个核心:Spring 声明式事务依赖代理和异常传播。代理没生效,事务不会进;异常没抛出去,事务不知道要回滚。

Transactional
方法被调用

是否经过
Spring 代理

事务不会生效

事务拦截器
开启事务

执行业务代码

异常是否
抛到代理层

代理认为成功
直接提交

异常是否符合
回滚规则

默认不回滚

回滚事务

场景一:异常被自己捕获了

业务方法执行

数据库扣减余额

发生异常

catch 捕获异常

是否重新抛出

事务代理
感知不到异常

提交事务

事务代理
捕获异常

回滚事务

事务通知要感知到目标方法抛出的异常,才能触发回滚。如果业务代码自己把异常吃掉了,外层事务代理就会认为方法正常结束,于是提交事务。

@Transactionalpublicvoidtransfer(Integerfrom,Integerto,BigDecimalmoney){try{accountMapper.decrease(from,money);inti=1/0;accountMapper.increase(to,money);}catch(Exceptione){e.printStackTrace();}}

这段代码的问题是catch之后没有继续抛异常。事务代理看不到异常,就不会回滚。

正确处理方式:

@Transactionalpublicvoidtransfer(Integerfrom,Integerto,BigDecimalmoney){try{accountMapper.decrease(from,money);inti=1/0;accountMapper.increase(to,money);}catch(Exceptione){thrownewRuntimeException(e);}}

或者直接不捕获,让异常自然抛出。

场景二:抛出了检查异常

Spring 默认只对RuntimeExceptionError回滚。像IOExceptionFileNotFoundException这类检查异常,默认不会触发回滚。

@TransactionalpublicvoidimportUser()throwsFileNotFoundException{userMapper.insert(user);newFileInputStream("not-exist.txt");}

如果希望检查异常也回滚,需要配置rollbackFor

@Transactional(rollbackFor=Exception.class)publicvoidimportUser()throwsFileNotFoundException{userMapper.insert(user);newFileInputStream("not-exist.txt");}

项目里建议直接写成:

@Transactional(rollbackFor=Exception.class)

这样比依赖默认规则更清楚。

场景三:方法不是 public

Spring 为方法创建事务代理时,通常要求被增强的方法是public。如果事务方法是默认访问级别、protectedprivate,事务可能不会按预期生效。

@Transactional(rollbackFor=Exception.class)voidupdateAccount(){accountMapper.update(account);}

应该改成:

@Transactional(rollbackFor=Exception.class)publicvoidupdateAccount(){accountMapper.update(account);}

这类问题非常隐蔽,因为代码能运行,但事务边界没有真正按你想的方式进入代理逻辑。

场景四:同类内部方法调用

这属于面试和项目里都很常见的扩展坑。Spring 事务通过代理对象生效,如果一个类内部直接用this调另一个带事务的方法,就绕过了代理。

@ServicepublicclassOrderService{publicvoidcreateOrder(){this.saveOrder();}@Transactional(rollbackFor=Exception.class)publicvoidsaveOrder(){orderMapper.insert(order);}}

createOrder()内部调用saveOrder(),没有经过 Spring 代理对象,事务可能不生效。

常见处理方式:

  1. 把事务方法拆到另一个 Service 中,通过 Spring 注入后调用。
  2. 让外层入口方法加事务。
  3. 必要时通过代理对象调用,但这通常不如拆分职责清楚。

场景五:数据库本身不支持事务

如果底层存储引擎不支持事务,Spring 再怎么加事务也没用。比如 MySQL 的 InnoDB 支持事务,MyISAM 不支持事务。

这类问题不一定是代码问题,而是表结构或数据库引擎选择问题。

快速排查清单

排查点怎么判断
注解是否加在 public 方法上检查方法访问修饰符
异常是否被吞掉catch后有没有重新抛出
异常类型是否会回滚检查是否配置rollbackFor
是否同类内部调用看调用链有没有经过 Spring 代理
数据库是否支持事务检查存储引擎和连接配置
方法是否由 Spring 容器管理检查对象是不是自己new出来的

面试回答模板

可以这样回答:

Spring 事务失效常见原因有几个。第一,异常被业务代码捕获后没有继续抛出,事务代理感知不到异常,所以不会回滚。第二,抛出的是检查异常,Spring 默认只回滚运行时异常,需要配置rollbackFor = Exception.class。第三,事务方法不是 public,代理增强可能不生效。第四,同一个类内部方法调用会绕过代理对象。还有一些场景比如数据库引擎不支持事务,或者对象不是由 Spring 容器管理,也会导致事务不生效。

小结

排查事务失效,不要只盯着@Transactional有没有写。

真正要看两条线:

  1. 调用有没有经过 Spring 代理。
  2. 异常有没有按回滚规则抛到事务代理。

只要这两条线断了,事务就可能失效。

http://www.jsqmd.com/news/839340/

相关文章:

  • PowerShdll源码深度分析:从DLL导出到控制台劫持的完整实现原理
  • react-photo-view 动画原理揭秘:从打开到关闭的完美过渡
  • YUV格式实战指南:从采样到存储的深度解析
  • Boost.Hana类型计算教程:从类型操作到高级元编程
  • 从零构建AI智能体:核心架构、ReAct模式与实战代码解析
  • 浏览器音乐解锁终极指南:3分钟轻松解密各大平台加密音频
  • Equalizer APO:解锁Windows系统级音频均衡的完整指南
  • 加油卡回收全流程:快速变现的实用攻略 - 团团收购物卡回收
  • 5个rc-form高级技巧:动态字段、异步验证、嵌套表单实战
  • 基于Python构建Telegram-AI桥接机器人:从架构设计到生产部署
  • 【MYSQL】在Centos7和ubuntu22.04环境下安装
  • Shermie-proxy:基于Node.js的脚本化HTTP/HTTPS代理调试工具实战指南
  • NotebookLM在博物馆学中的应用突破(2024国家一级馆实测数据首发)
  • 电赛小车结构避坑指南:从齿轮齿条到剪叉式,我们为什么最终选了舵机+剪叉方案?
  • 手把手教你学Simulink--电动物流车预充电路控制及主继电器粘连检测电机负载仿真
  • 佛山二手名表回收避坑攻略,内行教你避开黑心套路 - 奢侈品回收测评
  • 高效Vue代码差异对比插件:v-code-diff完整使用指南
  • 尚硅谷 Nginx 教程(亿级流量 Nginx 架构设计),基本使用,笔记 6-42
  • 5分钟打造专业直播间:OBS智能背景移除插件完全指南
  • DLSS Swapper:一键切换游戏DLSS版本,让NVIDIA显卡性能起飞
  • nvm-windows深度实战:Windows平台Node.js版本管理的系统化解决方案
  • 质粒测序数据自动化QC与比对分析:从Sanger测序到变异检测全流程
  • 解码FTP传输乱码:从Windows10 FTP 451错误看Unicode与多字节编码的世纪和解
  • 2026年石锅拌饭加盟厂家推荐:菏泽万华餐饮管理有限公司,石锅海鲜/石锅鱿鱼/石锅鸡/石锅豆腐/石锅菜/石锅鱼精选 - 品牌推荐官
  • 050二叉树中的最大路径和
  • 为Grok等大模型构建高效网页内容抓取与结构化提取工具
  • 重庆川岳机电设备:高新区性价比高的吊装搬运怎么联系 - LYL仔仔
  • PyInstaller Extractor终极指南:5分钟学会提取可执行文件源码
  • 从零构建私有数字保险库:硬件选型、加密策略与实战部署
  • FPGA深度学习加速器设计与能效优化实践