Spring 事务失效的常见场景
1. 引言Spring 事务管理是 Java 后端开发中最常用的能力之一但也是踩坑重灾区。很多同学在开发中遇到「明明加了Transactional数据却没回滚」的问题排查半天才发现是事务根本没生效。本文梳理 Spring 事务失效的常见场景从自调用、异常类型、多线程、传播行为与隔离级别等角度逐一剖析并给出可运行的实战示例。2. 自调用导致事务失效2.1 问题现象ServicepublicclassOrderService{TransactionalpublicvoidcreateOrder(Orderorder){// 业务逻辑updateStock(order.getProductId());}Transactional(propagationPropagation.REQUIRES_NEW)publicvoidupdateStock(LongproductId){// 扣减库存}}当外部调用createOrder时updateStock上的REQUIRES_NEW并不会生效。原因在于createOrder内部通过this.updateStock()调用走的是this引用而非 Spring 代理对象因此事务注解被绕过。2.2 原理分析Spring 事务基于 AOP 代理实现。只有通过代理对象调用方法时Transactional才会被拦截并织入事务逻辑。自调用this.method()直接调用目标对象自身的方法不经过代理事务注解自然失效。下面是自调用导致事务失效的调用关系图通过代理调用拦截 Transactionalthis.updateStock() 自调用绕过代理事务注解失效正常代理调用REQUIRES_NEW 生效REQUIRES_NEW 不生效外部调用方Spring 代理对象目标方法 createOrder()目标方法 updateStock()目标方法 updateStock()独立事务无独立事务图中实线表示正常经过代理的调用路径虚线表示自调用绕过代理的路径——createOrder内部通过this.updateStock()直接调用目标对象自身的方法没有经过 Spring 代理对象因此REQUIRES_NEW不会生效。2.3 解决方案方案一注入自身代理ServicepublicclassOrderService{AutowiredprivateOrderServiceself;TransactionalpublicvoidcreateOrder(Orderorder){// 业务逻辑self.updateStock(order.getProductId());}Transactional(propagationPropagation.REQUIRES_NEW)publicvoidupdateStock(LongproductId){// 扣减库存}}方案二拆分到不同 BeanServicepublicclassOrderService{AutowiredprivateStockServicestockService;TransactionalpublicvoidcreateOrder(Orderorder){// 业务逻辑stockService.updateStock(order.getProductId());}}方案三使用TransactionTemplate编程式事务ServicepublicclassOrderService{AutowiredprivateTransactionTemplatetransactionTemplate;publicvoidcreateOrder(Orderorder){transactionTemplate.execute(status-{// 业务逻辑updateStock(order.getProductId());returnnull;});}}3. 异常类型不对导致事务不回滚3.1 问题现象TransactionalpublicvoidcreateOrder(Orderorder){try{// 业务逻辑thrownewRuntimeException(库存不足);}catch(Exceptione){// 捕获异常后未抛出log.error(下单失败,e);}}事务方法内部捕获异常后没有重新抛出Spring 认为方法正常返回于是提交事务数据没有回滚。3.2 原理分析Spring 默认只对RuntimeException和Error回滚对受检异常Exception的子类默认不回滚。同时异常必须从Transactional方法中抛出才能触发回滚逻辑。3.3 解决方案方案一让异常抛出方法TransactionalpublicvoidcreateOrder(Orderorder){// 业务逻辑thrownewRuntimeException(库存不足);}方案二指定回滚异常类型Transactional(rollbackForException.class)publicvoidcreateOrder(Orderorder)throwsException{// 业务逻辑thrownewException(库存不足);}方案三手动回滚TransactionalpublicvoidcreateOrder(Orderorder){try{// 业务逻辑thrownewRuntimeException(库存不足);}catch(Exceptione){TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();log.error(下单失败,e);}}4. 多线程环境下事务不传播4.1 问题现象TransactionalpublicvoidbatchProcess(ListOrderorders){orders.parallelStream().forEach(order-{// 每个子线程中的数据库操作不在同一事务中updateOrder(order);});}4.2 原理分析Spring 事务默认绑定在当前线程的ThreadLocal中。新开的子线程无法继承父线程的事务上下文因此子线程中的数据库操作各自独立提交不参与父事务。4.3 解决方案方案一避免在事务中开多线程将多线程处理放在事务方法之外或使用TransactionTemplate在子线程中手动开启事务。方案二子线程中使用编程式事务publicvoidbatchProcess(ListOrderorders){orders.parallelStream().forEach(order-{transactionTemplate.execute(status-{updateOrder(order);returnnull;});});}方案三使用TransactionSynchronizationManager手动传递事务上下文不推荐复杂且易出错5. 传播行为与隔离级别实战5.1 传播行为速查传播行为说明REQUIRED默认当前有事务则加入没有则新建REQUIRES_NEW无论当前有无事务都新建一个独立事务SUPPORTS当前有事务则加入没有则以非事务方式执行NOT_SUPPORTED以非事务方式执行挂起当前事务MANDATORY当前必须有事务否则抛异常NEVER当前必须没有事务否则抛异常NESTED嵌套事务基于 Savepoint 实现5.2 隔离级别速查隔离级别说明DEFAULT使用数据库默认隔离级别READ_UNCOMMITTED读未提交存在脏读READ_COMMITTED读已提交解决脏读REPEATABLE_READ可重复读解决不可重复读SERIALIZABLE串行化解决幻读性能最低5.3 实战REQUIRES_NEW 与 REQUIRED 的区别ServicepublicclassOrderService{AutowiredprivateOrderDaoorderDao;AutowiredprivateLogServicelogService;TransactionalpublicvoidcreateOrder(Orderorder){orderDao.insert(order);// 即使这里抛出异常logService 的日志记录已独立提交logService.writeLog(创建订单order.getId());thrownewRuntimeException(业务异常);}}ServicepublicclassLogService{Transactional(propagationPropagation.REQUIRES_NEW)publicvoidwriteLog(Stringmessage){// 独立事务不受外层回滚影响}}5.4 实战NESTED 与 REQUIRES_NEW 的选择TransactionalpublicvoidcreateOrder(Orderorder){try{orderDao.insert(order);// NESTED外层回滚时这里也会回滚Savepoint 回滚// REQUIRES_NEW外层回滚时这里不会回滚couponService.deductCoupon(order.getCouponId());}catch(Exceptione){// 业务降级处理}}选择建议需要「部分回滚」且参与外层事务的最终一致性 → 使用NESTED。需要「独立提交」且不受外层事务影响如审计日志 → 使用REQUIRES_NEW。6. 事务失效排查清单遇到事务不生效的问题时可以按下面的清单逐项排查快速定位根因排查项快速验证方法常见误区1. 方法是否为public检查方法修饰符Transactional只对public方法生效误以为private/protected方法也能被代理拦截2. 是否通过代理调用确认调用方注入的是 Bean 而非this自调用会绕过代理在同类内部用this.method()调用事务注解失效3. 异常是否被吞掉检查方法内是否有try-catch且未重新抛出捕获异常后只记日志不抛出Spring 认为方法正常返回4. 异常类型是否为RuntimeException确认抛出的异常是RuntimeException或Error或已配置rollbackFor受检异常默认不回滚需显式指定rollbackFor Exception.class5. 是否在多线程中调用检查是否使用parallelStream、Async或手动new Thread子线程不共享父线程的ThreadLocal事务上下文6. 事务管理器是否配置正确确认DataSourceTransactionManager已注入且数据源正确多个数据源时未指定transactionManager导致事务管理错乱6. 总结Spring 事务失效的常见场景可以归纳为四类自调用this.method()绕过代理事务注解失效。异常类型不对受检异常默认不回滚异常被吞掉也不回滚。多线程事务上下文绑定线程子线程不共享父事务。传播行为与隔离级别使用不当REQUIRES_NEW与NESTED语义不同隔离级别影响并发一致性。掌握这些场景的成因与解决方案能帮助你在日常开发中快速定位事务问题写出更健壮的代码。

相关新闻

Madeira的dwrite unixlib如何拯救Steam文字渲染:一次静默失败的经典案例

Madeira的dwrite unixlib如何拯救Steam文字渲染:一次静默失败的经典案例

Madeira的dwrite unixlib如何拯救Steam文字渲染:一次静默失败的经典案例 【免费下载链接】Madeira Run x86-64 Windows PC games on jailed iOS via FEX-Emu Wine DXMT 项目地址: https://gitcode.com/GitHub_Trending/mad/Madeira Madeira 是一款让 iPhon…

2026/10/4 6:50:39 阅读更多 →
Windows WSL 用户必看:Token Monitor 无头 Agent 如何解决 SQLite 用量统计盲区

Windows WSL 用户必看:Token Monitor 无头 Agent 如何解决 SQLite 用量统计盲区

Windows WSL 用户必看:Token Monitor 无头 Agent 如何解决 SQLite 用量统计盲区 【免费下载链接】token-monitor Local-first desktop widget for tracking token usage, costs, and limits across 43 AI coding tools—including Claude Code, Codex, Cursor, Open…

2026/10/4 6:50:39 阅读更多 →
用Scratch做《天空之城》:从音符数据化到链表遍历与消息合奏的完整课堂实录

用Scratch做《天空之城》:从音符数据化到链表遍历与消息合奏的完整课堂实录

第一次带学生用Scratch做《天空之城》,我原以为是听力课的延伸,结果课上到一半变成了一堂数据结构课:一个个音符堆成旋律,一排排消息串成双人合奏,一条条链表把乐谱变成可以遍历的数据。很多老师把这节课归类为“Scrat…

2026/10/4 6:50:39 阅读更多 →

最新新闻

24天16万行代码:用Claude Code从零构建K12教育产品的实战工作流

24天16万行代码:用Claude Code从零构建K12教育产品的实战工作流

1. 先聊聊这个项目到底在做什么24 天,630 次提交,16 万行代码。这三个数字放在一起,任何一个写过代码的人都会先愣一下——平均每天 26 次提交、6600 多行代码产出,而且是在不到一个月的时间里从零搭出一个 K12 教育产品。如果这是…

2026/10/4 7:33:03 阅读更多 →
Codex++卡顿排查与提速:六个亲测有效的性能优化方案

Codex++卡顿排查与提速:六个亲测有效的性能优化方案

1. 卡顿先别急着换工具,先搞清楚Codex到底卡在哪我用Codex做日常开发也有小半年了,这工具的补全和对话能力都相当能打,尤其是结合多文件上下文理解之后,改起老项目来确实顺手。但最近这阵子,明显感觉到它变“钝”了&am…

2026/10/4 7:33:03 阅读更多 →
OpenRig:基于Node.js与tmux的Codex CLI本地化封装方案

OpenRig:基于Node.js与tmux的Codex CLI本地化封装方案

1. OpenRig 是什么:一个被误读但极具潜力的开发者工具链OpenRig 这个名字最近在开发者社区里频繁出现,但它既不是某个新发布的 AI 框架,也不是某家大厂推出的闭源 SDK。我第一次在 GitLab CI 日志里看到openrig被当作服务名调用时&#xff0c…

2026/10/4 7:33:03 阅读更多 →
别急着做完整SaaS:用AI Agent快速验证产品需求

别急着做完整SaaS:用AI Agent快速验证产品需求

1. 为什么“先做完整 SaaS”正在变成一条弯路过去几年,做产品的默认路径几乎是固定的:先想清楚需求,画原型,搭后端,做权限,接支付,再一点点把功能堆成一个完整的 SaaS。这套打法在移动互联网时代…

2026/10/4 7:33:03 阅读更多 →
openrig:统一管理Claude Code与Codex的YAML配置编排工具

openrig:统一管理Claude Code与Codex的YAML配置编排工具

1. openrig 到底在解决什么问题第一次看到 openrig 这个名字,很多人会以为是某个硬件机架项目,或者跟 rigging 动画绑定有关。但结合它周边的关键词——claude code、codex、yaml、node.js——基本可以判断,这是一个围绕 AI 编程助手做配置编…

2026/10/4 7:33:03 阅读更多 →
两千元预算本地部署Qwen3-27B:V100实战280 tok/s推理

两千元预算本地部署Qwen3-27B:V100实战280 tok/s推理

1. 两千块预算的本地AI部署,到底能跑出什么水平先说结论:两千多块钱,在二手市场上凑一套能跑Qwen3-27B级别模型、推理速度稳定超过280 tok/s的平台,这件事在2025年是完全可行的。我自己前前后后折腾了大概三周,从选卡、…

2026/10/4 7:32:02 阅读更多 →

日新闻

KT148A语音芯片外挂8002D功放的工程实践指南

KT148A语音芯片外挂8002D功放的工程实践指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/4 1:00:58 阅读更多 →
LLC谐振变换器增益公式推导:从FHA等效到完整归一化表达式

LLC谐振变换器增益公式推导:从FHA等效到完整归一化表达式

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/4 1:00:58 阅读更多 →
ARM架构深度解析:从RISC设计理念到交叉编译实战

ARM架构深度解析:从RISC设计理念到交叉编译实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/4 1:00:58 阅读更多 →

周新闻

KT148A语音芯片外挂8002D功放的工程实践指南

KT148A语音芯片外挂8002D功放的工程实践指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/4 1:00:58 阅读更多 →
LLC谐振变换器增益公式推导:从FHA等效到完整归一化表达式

LLC谐振变换器增益公式推导:从FHA等效到完整归一化表达式

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/4 1:00:58 阅读更多 →
ARM架构深度解析:从RISC设计理念到交叉编译实战

ARM架构深度解析:从RISC设计理念到交叉编译实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/4 1:00:58 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/2 10:36:31 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/3 9:42:35 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/3 9:42:36 阅读更多 →