一文详解Spring 事务失效场景
前提Spring 事务底层依靠AOP 动态代理只有代理对象调用方法才会触发事务增强同时事务生效需要满足数据库引擎支持事务InnoDBMyISAM 不支持。注解Transactional1. 方法不是 public最常见Spring AOP 只拦截public方法private/protected/default方法加注解完全无效。Service public class UserService { // ❌ private事务失效 Transactional private void addUser() { userMapper.insert(...); int i 1/0; // 抛异常不会回滚 } }2. 同类内部方法调用this 调用不走代理在同一个类中直接this.xxx()调用带Transactional的方法不是代理对象调用AOP 不会切入。Service public class UserService { public void test() { // ❌ this调用不是代理对象addUser事务失效 this.addUser(); } Transactional public void addUser() { userMapper.insert(...); int i 1/0; // 异常不回滚 } }✅ 解决方案自身注入UserService self;self.addUser()。3. 异常类型不对捕获了异常 / 抛出非回滚异常Transactional默认只对RuntimeException 和 Error触发回滚受检异常Exception默认不回滚。场景 3.1 自己 try-catch 吃掉异常Transactional public void addUser() { try { userMapper.insert(...); int i 1/0; } catch (Exception e) { // ❌ 异常被捕获没有抛出给AOP事务不会回滚 log.error(error, e); } }场景 3.2 抛出受检异常没指定 rollbackForTransactional public void addUser() throws IOException { userMapper.insert(...); throw new IOException(IO异常); // ❌ IOException是受检异常默认不回滚 }✅ 修复Transactional(rollbackFor Exception.class)4. 传播行为配置错误典型REQUIRES_NEW独立事务SUPPORTS非事务环境下就是无事务NOT_SUPPORTED 强制以非事务运行。// ❌ NOT_SUPPORTED不开启事务发生异常不会回滚 Transactional(propagation Propagation.NOT_SUPPORTED) public void addUser(){ userMapper.insert(...); int i1/0; }5. 数据库底层不支持事务MyISAM 引擎不支持事务哪怕代码写对Transactional也无法回滚。查看表引擎show create table user;✅ 必须使用 InnoDB。6. 多线程场景事务失效事务绑定在当前线程新开子线程执行数据库操作子线程是全新事务上下文和主线程事务隔离。Transactional public void test() { userMapper.insertA(); new Thread(() - { userMapper.insertB(); int i1/0; // ❌ 子线程异常不会回滚insertA }).start(); }7. 事务超时时间超时自动回滚不是不生效是触发回滚// 5秒超时超时后自动回滚 Transactional(timeout 5) public void test() throws InterruptedException { userMapper.insert(); Thread.sleep(6000); }8. 注解被错误放在接口上老版本 SpringSpring AOP 优先读取实现类上的注解只在接口上加Transactional使用 CGLIB 代理时事务会失效。最佳实践Transactional写在Service 实现类的 public 方法。9. 嵌套事务传播 NESTED外层捕获异常导致内层不回滚Service public class UserService { Autowired private OrderService orderService; Transactional public void main() { userMapper.insertUser(); try { // NESTED 嵌套事务 orderService.addOrder(); } catch (Exception e) { log.error(子事务异常); // ❌ 外层捕获异常外层事务不回滚内层子事务回滚 } } } Service class OrderService{ Transactional(propagation Propagation.NESTED) public void addOrder(){ orderMapper.insert(); int i1/0; } }总结AOP 代理问题非 public、同类 this 调用异常问题吞异常、rollbackFor 没配置数据库MyISAM 引擎传播属性NOT_SUPPORTED/SUPPORTS 误用线程跨线程事务隔离注解位置仅接口上加注解CGLIB扩展Q为什么同类 this 调用事务失效ASpring 事务由 AOP 代理对象增强this 是原始对象没有经过代理不会执行事务拦截器。QrollbackForException.class 作用A默认只回滚 RuntimeException/Error加上之后所有 Exception包含受检异常都会触发回滚。Spring 事务传播行为Propagation核心多个事务方法相互调用时事务如何传递一共 7 种传播类型含义场景说明REQUIRED默认如果当前存在事务就加入该事务没有则新建一个事务绝大多数业务同一件事要么全部成功要么全部回滚REQUIRES_NEW新建独立事务如果当前有事务挂起原有事务执行新事务互不影响日志记录、独立审计子事务失败不影响外层事务SUPPORTS当前有事务就加入没有事务就以非事务方式执行查询场景可有可无事务MANDATORY必须运行在已有事务中当前无事务直接抛异常强制外层必须开启事务防止单独调用NOT_SUPPORTED以非事务方式执行当前有事务就挂起原有事务大批量查询不想占用事务资源NEVER强制非事务运行如果当前存在事务直接抛异常禁止事务环境只读统计NESTED嵌套事务有事务则嵌套在当前事务内保存点 savepoint无事务新建事务子事务回滚不会导致外层事务回滚外层事务回滚嵌套子事务一定回滚重点区分REQUIRES_NEW vs NESTEDREQUIRES_NEW两个完全独立事务外层事务挂起。子事务提交后不受外层影响子事务异常外层可以捕获继续提交。NESTED同一个物理事务基于保存点 savepoint。只有外层事务提交嵌套事务才真正提交外层回滚嵌套一定回滚。NESTED 不是独立事务是保存点REQUIRES_NEW 是全新独立事务。扩展传播行为默认值REQUIREDREQUIRES_NEW 挂起事务原理Spring 事务管理器暂停当前事务保存上下文执行完恢复哪些传播行为会创建新事务REQUIRED、REQUIRES_NEW、NESTEDSpring 7 种事务传播行为 代码演示 回滚规则前提AOP 代理调用才生效同类方法内部调用事务失效。Transactional默认只对RuntimeException / Error触发回滚受检异常默认不回滚。外层方法叫内层方法外层 调用方内层 被调用方。公共基础代码两个 Service方便测试Service public class OuterService { Autowired private InnerService innerService; // 外层方法 public void outer(){} } Service public class InnerService { // 内层方法 public void inner(){} }1. REQUIRED默认有事务就加入当前事务无事务新建事务。外层内层是同一个事务Service public class OuterService { Autowired private InnerService innerService; Transactional(propagation Propagation.REQUIRED) public void outer() { // 插入订单 innerService.inner(); // 外层抛异常 throw new RuntimeException(外层异常); } } Service public class InnerService { Transactional(propagation Propagation.REQUIRED) public void inner() { // 扣库存 } }✅ 回滚规则外层抛异常 →整个事务回滚内层操作回滚内层抛异常外层不捕获 →整个事务回滚内层抛异常外层 try-catch 接住依然会回滚异常标记了事务为 rollback-only外层 catch 后提交时报异常。场景正常业务增删改同业务要么全成要么全回。2. REQUIRES_NEW如果外层有事务挂起外层事务新建独立事务。两个完全独立物理事务互不干扰。Service public class OuterService { Autowired private InnerService innerService; Transactional public void outer() { // 插入订单 try { innerService.inner(); } catch (Exception e) { // 捕获内层异常外层继续跑 } // 外层正常结束外层事务提交 } } Service public class InnerService { Transactional(propagation Propagation.REQUIRES_NEW) public void inner() { // 扣库存 throw new RuntimeException(内层异常); } }✅ 回滚规则内层抛异常 →内层事务回滚外层事务不受影响可以正常提交try-catch 捕获内层异常外层抛异常 →外层事务回滚内层事务已经独立提交完成不会回滚重点内层事务执行完就提交外层事务被挂起两者独立。 适用操作日志、审计记录就算主业务失败日志也要入库。3. SUPPORTS当前有事务就加入事务没有事务就非事务方式运行Service public class OuterService { Autowired private InnerService innerService; Transactional public void outer() { // 外层存在事务 innerService.inner(); } } Service public class InnerService { Transactional(propagation Propagation.SUPPORTS) public void inner() { // 查询或者更新 } }✅ 回滚规则外层有事务内层加入外层事务任意地方异常整体回滚和 REQUIRED 行为一致外层没有事务内层无事务发生异常不会回滚已经执行的 SQL 持久化适用查询方法有事务环境就参与事务没有就普通查询。4. MANDATORY必须运行在已有事务中外层没有事务直接抛异常不会新建事务Service public class OuterService { Autowired private InnerService innerService; // 外层不加 Transactional public void outer() { innerService.inner(); // ❌ 直接抛出 IllegalTransactionStateException } } Service public class InnerService { Transactional(propagation Propagation.MANDATORY) public void inner() { } }✅ 回滚规则外层存在事务内层加入外层事务统一回滚外层无事务直接报错方法不执行。适用强制该方法必须被事务方法调用防止单独调用造成数据不一致。5. NOT_SUPPORTED以非事务方式执行外层有事务就挂起外层事务内层不在事务里挂起suspend ≠ 提交 / 回滚外层事务只是临时把外层事务暂停、放到一边保存起来然后执行当前这个无事务的方法等内层方法执行完毕之后再恢复外层事务继续跑。概括外层事务先暂停内层裸跑没有事务内层跑完再把外层事务捡回来继续执行。Service public class OuterService { Autowired private InnerService innerService; Transactional public void outer() { // 外层事务开启调用内层时外层事务被挂起 innerService.inner(); // 内层执行完恢复外层事务 } } Service public class InnerService { Transactional(propagation Propagation.NOT_SUPPORTED) public void inner() { // 内层无事务这里抛异常内层SQL不会回滚 throw new RuntimeException(); } }✅ 回滚规则内层代码抛异常内层操作不会回滚无事务外层捕获异常后外层事务继续提交外层没捕获外层事务回滚。适用大批量查询大报表不想占用事务连接。6. NEVER禁止在事务中执行外层存在事务直接抛异常必须非事务环境Service public class OuterService { Autowired private InnerService innerService; Transactional public void outer() { innerService.inner(); // ❌ 抛出 IllegalTransactionStateException } } Service public class InnerService { Transactional(propagation Propagation.NEVER) public void inner() { } }✅ 回滚规则外层有事务直接报错外层无事务普通非事务执行异常不会回滚。适用统计任务明确禁止事务环境。7. NESTED 嵌套事务外层有事务基于 JDBC Savepoint 保存点嵌套在同一个物理事务外层无事务新建事务。 ⚠️ 和 REQUIRES_NEW 最大区别同一个物理事务不是独立事务Service public class OuterService { Autowired private InnerService innerService; Transactional public void outer() { // 外层事务开启创建savepoint try { innerService.inner(); } catch (Exception e) { // 捕获内层异常只回滚到savepoint外层事务还能继续提交 System.out.println(内层异常但是外层可以提交); } // 外层正常执行最终提交 } } Service public class InnerService { Transactional(propagation Propagation.NESTED) public void inner() { throw new RuntimeException(内层嵌套异常); } }✅ 回滚规则内层抛异常外层 try-catch 捕获只回滚内层操作外层事务可以正常提交 ✅外层抛异常无论内层是否成功外层整体回滚内层 savepoint 一起回滚底层JDBC Savepoint不是独立事务。MySQL InnoDB 支持。适用主流程不能回滚但子步骤失败可以单独撤销子步骤。 核心对比总结表传播行为外层有事务外层无事务独立事务内层异常 (外层 catch)外层异常REQUIRED加入外层事务新建事务否标记 rollback-only整体回滚整体回滚REQUIRES_NEW挂起外层新建独立事务新建事务是内层回滚外层可提交外层回滚内层已提交不受影响SUPPORTS加入外层事务非事务执行否整体回滚整体回滚MANDATORY加入外层事务抛异常否整体回滚整体回滚NOT_SUPPORTED挂起外层内层非事务非事务执行否内层不回滚外层回滚内层已持久化NEVER抛异常非事务执行否无机会执行无机会执行NESTED创建 savepoint 嵌套新建事务否仅回滚 savepoint外层可提交外层整体回滚

相关新闻

LCR斐波那契数感想

LCR斐波那契数感想

这次学习用循环计算斐波那契数列,重点弄懂了for循环的逻辑。起初我不清楚循环里面数值该怎么变化,一步步推演才看懂,每一轮循环都会更新数字,不断向后算出数列下一项。我还对初始值、空变量的疑问慢慢解开。我认识到可以手动模拟循…

2026/10/9 16:40:20 阅读更多 →
ArtCraft Rust工作区深度解析:Tauri桌面应用、9大Provider客户端与SQLite任务系统如何协作

ArtCraft Rust工作区深度解析:Tauri桌面应用、9大Provider客户端与SQLite任务系统如何协作

ArtCraft Rust工作区深度解析:Tauri桌面应用、9大Provider客户端与SQLite任务系统如何协作 【免费下载链接】artcraft ArtCraft is an intentional crafting engine for artists, designers, and filmmakers 项目地址: https://gitcode.com/gh_mirrors/ar/artcraf…

2026/10/10 19:41:00 阅读更多 →
汽车内饰护理翻新指南:按材质分部位清洁、养护与翻新

汽车内饰护理翻新指南:按材质分部位清洁、养护与翻新

1. 引言 汽车内饰用久了,真正变差的是材质老化:真皮发硬开裂、塑料件发白、绒面藏味、缝隙积灰。恢复质感、延缓老化,核心是按需按材质分部位清洁、养护和翻新,而不是一股脑全换。 2. 先搞清楚:你的内饰"病"…

2026/10/9 16:40:44 阅读更多 →

最新新闻

【Claude Code】“源码”解读(一):架构总览与设计哲学——为什么 Claude 和 GPT 不一样?TaoToken 统一 Key 通道的接入视角

【Claude Code】“源码”解读(一):架构总览与设计哲学——为什么 Claude 和 GPT 不一样?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/12 10:44:39 阅读更多 →
GoWind Admin:内置微服务接口聚合的企业级全栈中后台框架

GoWind Admin:内置微服务接口聚合的企业级全栈中后台框架

1. 项目概述:这套全栈中后台框架到底解决什么问题做中后台开发这么多年,我最大的感受是:大部分企业项目真正花时间的不是业务逻辑本身,而是那些“看起来很简单、做起来全是坑”的基础设施——登录鉴权、权限路由、菜单管理、用户角…

2026/10/12 10:44:39 阅读更多 →
不加班也能持续成长:AI工程师的高效学习与时间管理实战指南

不加班也能持续成长:AI工程师的高效学习与时间管理实战指南

先给自己泼一盆冷水:作为一名在职AI工程师,你大概率已经发现一个扎心的事实——技术栈更新快到你还没把上一套框架吃透,新一代大模型又已经刷屏了。GitHub上每天上百个新仓库冒出来,ArXiv上论文刷得比朋友圈还勤快。如果不持续学习…

2026/10/12 10:44:39 阅读更多 →
cProfile报错ProfilerNotRunningError:原因、排查与正确用法

cProfile报错ProfilerNotRunningError:原因、排查与正确用法

1. 报错现场:一个看似“莫名其妙”的性能分析异常先看一段触发了这个报错的代码:import cProfileprof cProfile.Profile() # 注意:这里没有调用 prof.enable() 或 prof.enable() 在别处被调用了但没有生效 # ... 执行需要分析的代码 ... pro…

2026/10/12 10:44:39 阅读更多 →
基于SSM与MySQL的网上选课系统设计与实现

基于SSM与MySQL的网上选课系统设计与实现

1. 网上选课系统的核心需求与设计思路先说结论:网上选课系统的本质,就是把传统教务场景里的人工选课流程搬到线上,让"课程资源分配"这件事变得更透明、更高效。听起来简单,但真正动手写代码之前,如果不对需求…

2026/10/12 10:44:39 阅读更多 →
Spring @Required 注解废弃解析:依赖注入校验演进与构造器注入迁移

Spring @Required 注解废弃解析:依赖注入校验演进与构造器注入迁移

1. 一个被遗忘的注解,为什么还值得聊Required这个注解,如果你是从 Spring Boot 时代入行的,大概率没见过它,甚至在 IDE 里敲出来会发现它上面压着一道醒目的删除线。它属于那种"存在于文档里、消失在代码里"的元老级 AP…

2026/10/12 10:43:37 阅读更多 →

日新闻

复古胶片颗粒感噪点合成器:Canvas ImageData 像素高斯杂色注入算法

复古胶片颗粒感噪点合成器:Canvas ImageData 像素高斯杂色注入算法

在数码相机、高清显示屏与现代矢量图形技术高度发达的今天,画面可以做到绝对的锐利、平滑与无瑕。然而,当一张秋日手账插画或拍立得照片过于“平整无瑕”时,往往会散发出一种冰冷生硬的“数码塑料感(Digital Plasticity&#xff0…

2026/10/12 0:00:59 阅读更多 →
活字印刷古籍线装排版:Canvas 竖排文字与栏线自适应算法

活字印刷古籍线装排版:Canvas 竖排文字与栏线自适应算法

在现代网页与移动端设计中,横排(Horizontal Layout)早已经成为了绝对的主流。然而,当我们翻开泛黄的线装古籍、宋版木刻诗集,或是欣赏一张茶道雅集的手写便签时,那种**自上而下纵向书写、自右向左逐列铺展&…

2026/10/12 0:00:59 阅读更多 →
周日晚间的“精神松绑减震器”:无压力情绪倾倒箱与温和轻声陪伴

周日晚间的“精神松绑减震器”:无压力情绪倾倒箱与温和轻声陪伴

每到周日的晚上八点到十点,很多人心里都会悄悄亮起一盏警示灯。 在心理学上,这种现象有一个专门的称谓——“周日夜晚焦虑症(Sunday Scaries)”。明天又是周一,闹钟又要重新在七点响彻卧房;脑海里仿佛有一个…

2026/10/12 0:00:59 阅读更多 →

周新闻

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

简介:基于 ARIMA、LSTM、Transformer 等模型的流感时间序列预测 Python 源码,面向计算机相关专业课程设计与期末大作业学生,以及项目实战学习者。内容覆盖预处理、平稳性检验、定阶、残差分析、多模型对比预测的完整时序建模流程,…

2026/10/12 0:16:30 阅读更多 →
影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别 做影刀RPA自动化,十个新手有八个栽在"往输入框里填东西"这件事上:要么填不进去,要么填了一半,要么直接把原来内容追加在后面。这背后的根因&…

2026/10/12 0:16:38 阅读更多 →
影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容 1. 认识影刀:什么场景该用RPA采小说数据 起点中文网的页面结构相对稳定——分类榜单、书籍详情、章节内容三块独立页面,跳转链路清晰。这种场景非常适合影刀自动化&#x…

2026/10/12 0:16:43 阅读更多 →

月新闻

我发现了一个新思路:用 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/11 10:45:37 阅读更多 →
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/11 14:36:53 阅读更多 →
黑夜航拍船只数据集训练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/11 14:36:54 阅读更多 →