Spring Boot 事务失效的常见坑,我一次性给你讲清楚
Spring Boot 事务失效的常见坑我一次性给你讲清楚说出来你可能不信我被 Spring Boot 事务坑过三次才长记性。第一次是Transactional放在私有方法上事务根本没生效我还傻傻地查了一整天数据库日志。第二次是同一个类里调用带事务的方法自调用导致事务失效我还以为是我代码写错了。第三次更离谱异常被 catch 吞掉了事务居然正常提交了。今天就把这些坑一次性讲清楚保证你以后不再踩。坑一Transactional 放在私有方法上这是我踩过的第一个坑。当时我写了这么一段代码ServicepublicclassOrderService{AutowiredprivateOrderMapperorderMapper;publicvoidcreateOrder(Orderorder){// 业务逻辑saveOrder(order);}TransactionalprivatevoidsaveOrder(Orderorder){orderMapper.insert(order);// 其它数据库操作}}问题描述我满心以为saveOrder方法会自动开启事务结果订单数据妥妥地入库了但是事务根本没生效。如果后面发生异常数据已经提交了根本回滚不了。原因分析Spring 的 AOP 代理机制是通过代理对象调用目标方法来实现增强的而私有方法无法被代理。所以Transactional放在private方法上完全无效。解决方案把Transactional放在public方法上或者抽取到另一个 Service 中因为 AOP 只能拦截外部调用。正确代码示例ServicepublicclassOrderService{AutowiredprivateOrderMapperorderMapper;// ✅ 正确Transactional 放在 public 方法上TransactionalpublicvoidcreateOrder(Orderorder){orderMapper.insert(order);// 其它数据库操作}}或者抽取到另一个 ServiceServicepublicclassOrderDaoService{AutowiredprivateOrderMapperorderMapper;TransactionalpublicvoidsaveOrder(Orderorder){orderMapper.insert(order);}}ServicepublicclassOrderService{AutowiredprivateOrderDaoServiceorderDaoService;publicvoidcreateOrder(Orderorder){// 业务逻辑orderDaoService.saveOrder(order);}}坑二同一个类里自调用这个坑特别隐蔽很多人写了几年代码都不一定知道。看看下面的代码ServicepublicclassUserService{publicvoidregister(Useruser){// 前置验证validate(user);// 创建用户createUser(user);}Transactionalprivatevoidvalidate(Useruser){// 检查用户名是否已存在if(userMapper.findByName(user.getName())!null){thrownewRuntimeException(用户名已存在);}}TransactionalprivatevoidcreateUser(user){userMapper.insert(user);}}问题描述这段代码看起来没问题吧但实际上validate和createUser方法上的事务根本不会生效因为register方法调用this.validate()和this.createUser()是内部调用不会触发 AOP 代理。原因分析Spring 事务是基于 AOP 代理实现的只有通过代理对象调用方法才会触发增强。同一类中的方法调用是直接调用不经过代理所以Transactional注解会被忽略。解决方案将方法抽取到另一个 Service通过注入的方式调用注入自身代理对象self来调用正确代码示例方案一抽取 ServiceServicepublicclassUserService{AutowiredprivateUserValidationServicevalidationService;AutowiredprivateUserCreationServicecreationService;publicvoidregister(Useruser){validationService.validate(user);creationService.createUser(user);}}ServicepublicclassUserValidationService{Transactionalpublicvoidvalidate(Useruser){// 验证逻辑}}ServicepublicclassUserCreationService{TransactionalpublicvoidcreateUser(Useruser){// 创建逻辑}}正确代码示例方案二自注入ServicepublicclassUserServiceimplementsApplicationContextAware{privateApplicationContextapplicationContext;OverridepublicvoidsetApplicationContext(ApplicationContextcontext){this.applicationContextcontext;}publicvoidregister(Useruser){// 通过代理对象调用事务生效UserServiceselfapplicationContext.getBean(UserService.class);self.validate(user);self.createUser(user);}Transactionalpublicvoidvalidate(Useruser){// 验证逻辑}TransactionalpublicvoidcreateUser(Useruser){// 创建逻辑}}坑三异常被 catch 吞掉这个坑我愿称之为最冤的坑因为代码看起来完全正常ServicepublicclassPaymentService{Transactionalpublicvoidpay(Orderorder){try{// 扣减库存productService.reduceStock(order.getProductId(),order.getQuantity());// 更新订单状态orderMapper.updateStatus(order.getId(),PAID);}catch(Exceptione){log.error(支付失败,e);// 异常被吞掉了}}}问题描述如果reduceStock抛出异常被 catch 住事务居然正常提交了库存没扣减订单状态却变成已支付这不完犊子了吗原因分析Spring 事务默认只对**未捕获的运行时异常RuntimeException**回滚。如果异常被 catch 住Spring 认为异常已处理事务不会回滚。解决方案在 catch 块中重新抛出异常使用rollbackFor指定回滚的异常类型正确代码示例ServicepublicclassPaymentService{Transactional(rollbackForException.class)publicvoidpay(Orderorder){try{productService.reduceStock(order.getProductId(),order.getQuantity());orderMapper.updateStatus(order.getId(),PAID);}catch(Exceptione){log.error(支付失败,e);// ✅ 重新抛出异常让事务感知到thrownewRuntimeException(支付失败,e);}}}或者更简洁的写法ServicepublicclassPaymentService{Transactional(rollbackForException.class)publicvoidpay(Orderorder){productService.reduceStock(order.getProductId(),order.getQuantity());orderMapper.updateStatus(order.getId(),PAID);}}坑四传播行为配置错误有时候你可能遇到这种情况方法 A 调用方法 B希望 B 在新事务中运行结果 B 和 A 在同一个事务里ServicepublicclassAccountService{Transactionalpublicvoidtransfer(LongfromId,LongtoId,BigDecimalamount){// 扣款accountMapper.decrease(fromId,amount);// 转账希望独立事务paymentService.payment(toId,amount);// 故意抛错测试回滚thrownewRuntimeException(测试);}}ServicepublicclassPaymentService{Transactional(propagationPropagation.REQUIRES_NEW)publicvoidpayment(LongtoId,BigDecimalamount){accountMapper.increase(toId,amount);}}问题描述我期望payment方法在独立事务中运行这样即使transfer回滚payment的操作也不会被回滚。结果测试发现payment居然和transfer在同一个事务里一起回滚了。原因分析这是因为payment是通过this.payment()内部调用的没有走 AOP 代理所以REQUIRES_NEW传播行为失效。解决方案确保内部调用也通过代理对象或者将方法放到不同的 Service 中。ServicepublicclassAccountService{AutowiredprivatePaymentServicepaymentService;Transactionalpublicvoidtransfer(LongfromId,LongtoId,BigDecimalamount){accountMapper.decrease(fromId,amount);// ✅ 通过注入的 Bean 调用走 AOP 代理paymentService.payment(toId,amount);thrownewRuntimeException(测试);}}写在最后Spring Boot 事务失效的坑总结下来就是四点私有方法上放注解— 代理不了事务无效同一类中自调用— 不经过代理事务无效异常被 catch 吞掉— Spring 感知不到不会回滚传播行为配错了— 内部调用导致传播行为失效记住一句话事务生效的关键是 AOP 代理只要记住这一点所有事务失效的问题都能找到根源。建议大家写完带Transactional的方法后一定一定要测试一下异常情况下能不能回滚别像我一样踩坑踩三次才长记性。如果觉得有帮助点个赞再走呗。有任何问题评论区见

相关新闻

学霸同款 9个降AIGC工具测评:专科生降AI率必备攻略

学霸同款 9个降AIGC工具测评:专科生降AI率必备攻略

在当前的学术写作环境中,AI生成内容(AIGC)已成为论文审查的重要关注点。随着高校对学术原创性的重视程度不断提升,如何有效降低AIGC率、去除AI痕迹,同时保持文章的逻辑性和语义通顺,成为了许多专科生面临的…

2026/7/5 1:36:01 阅读更多 →
电池充电比较:PID与电流控制器(Matlab代码实现)

电池充电比较:PID与电流控制器(Matlab代码实现)

💥💥💞💞欢迎来到本博客❤️❤️💥💥 🏆博主优势:🌞🌞🌞博客内容尽量做到思维缜密,逻辑清晰,为了方便读者。 ⛳️座右铭&a…

2026/7/3 3:29:11 阅读更多 →
表贴式PMSM的直接转矩控制(DTC)仿真模型(Simulink仿真实现)

表贴式PMSM的直接转矩控制(DTC)仿真模型(Simulink仿真实现)

💥💥💞💞欢迎来到本博客❤️❤️💥💥 🏆博主优势:🌞🌞🌞博客内容尽量做到思维缜密,逻辑清晰,为了方便读者。 ⛳️座右铭&a…

2026/7/4 8:52:00 阅读更多 →

最新新闻

我第一次用 Codex,差点把桌面交给它

我第一次用 Codex,差点把桌面交给它

CODEX 第三期 写在前面 这不是一篇炫技教程。它只解决小白第一次用 Codex 时最容易忽略的一件事:不要急着把桌面、客户资料和真实项目交给 AI,先用一个安全小文件夹跑通入门闭环。 我第一次打开 Codex 的时候,差点犯一个很蠢的错误。 不是装错版本,也不是登录失败。 而…

2026/7/5 13:20:08 阅读更多 →
AI写专著全流程解析,利用工具轻松打造20万字专业专著!

AI写专著全流程解析,利用工具轻松打造20万字专业专著!

对于很多研究者来说,写学术专著时最让人头疼的,莫过于“有限的时间”与“无限的需求”之间的矛盾。撰写专著通常需要数年时间,而研究者还要兼顾教学、科研、学术交流等各种任务,能够专心写作的时间往往是零散的。这种零碎的写作方…

2026/7/5 13:20:08 阅读更多 →
《唤醒你的AI同事:WorkBuddy从零上手》037:附录B 快捷键一览

《唤醒你的AI同事:WorkBuddy从零上手》037:附录B 快捷键一览

本文是《唤醒你的 AI 同事——WorkBuddy 从零上手》系列 第 37 篇。 回顾总结:通过第 036 篇附录 A,我们整理了 WorkBuddy 最实用的指令模板——从报告撰写、合同审查到数据分析、代码生成等 10+ 个场景。你现在已经拥有了即拿即用的"武器库"。但光有模板还不够,手…

2026/7/5 13:20:08 阅读更多 →
零日漏洞攻防实战:从检测到响应的纵深防御体系构建

零日漏洞攻防实战:从检测到响应的纵深防御体系构建

1. 项目概述:直面数字世界的“隐形杀手”在网络安全这个没有硝烟的战场上,最让防御者感到棘手的,往往不是那些已知的、有补丁可循的威胁,而是那些被称为“零日漏洞”的未知攻击。从业十几年,我处理过无数次安全事件&am…

2026/7/5 13:16:07 阅读更多 →
多人聊天室

多人聊天室

一、项目简介本项目是一个基于Java Swing MySQL的博客文章管理系统,实现了文章发布、分类管理、用户登录、全局搜索等核心功能。 我在项目中主要负责全局搜索模块、数据库读写层设计以及部分面向对象架构设计工作。二、个人任务简述序号完成功能与任务描述1全局搜索…

2026/7/5 13:14:06 阅读更多 →
骑乘无忧怎么选 (新手女生小个子巡航摩托)选购要点

骑乘无忧怎么选 (新手女生小个子巡航摩托)选购要点

入手自动挡巡航摩托,CVT 和 AMT 该怎么选?面向入门骑手、女性车友以及身高娇小的人群,最优方案已然明确。AMT 巡航操控顺手、动力充沛、使用便捷,外观也十分出彩,是综合实力更强的选择。QJMOTOR 闪 300AMT 与闪 400AMT…

2026/7/5 13:14:06 阅读更多 →

日新闻

B站视频下载神器BiliTools:5分钟学会轻松保存任何B站内容

B站视频下载神器BiliTools:5分钟学会轻松保存任何B站内容

B站视频下载神器BiliTools:5分钟学会轻松保存任何B站内容 【免费下载链接】BiliTools A cross-platform bilibili toolbox. 跨平台哔哩哔哩工具箱,支持下载视频、番剧等等各类资源 项目地址: https://gitcode.com/GitHub_Trending/bilit/BiliTools …

2026/7/5 0:03:34 阅读更多 →
威胁模型全解析:从新手入门到实战应用,助你构建安全产品!

威胁模型全解析:从新手入门到实战应用,助你构建安全产品!

威胁模型的陌生现状在忙碌疲惫的一天里,参与了关于混合后量子密码学的讨论,应付端点攻击找茬的人,还参与留言板讨论后,发现“威胁模型”对多数人仍是陌生概念,且多被当作时髦用语。有趣的相关画作有一幅由 Embyr 创作的…

2026/7/5 0:03:34 阅读更多 →
渗透测试入门指南:从零基础到实战环境搭建

渗透测试入门指南:从零基础到实战环境搭建

1. 从“看热闹”到“入门”:我理解的渗透测试到底是什么?每次看到新闻里说某个大公司的数据被“黑”了,或者某个网站被攻击导致服务瘫痪,你是不是和我一样,心里会冒出两个念头:一是“这黑客真厉害”&#x…

2026/7/5 0:07:38 阅读更多 →

周新闻

B站视频下载神器BiliTools:5分钟学会轻松保存任何B站内容

B站视频下载神器BiliTools:5分钟学会轻松保存任何B站内容

B站视频下载神器BiliTools:5分钟学会轻松保存任何B站内容 【免费下载链接】BiliTools A cross-platform bilibili toolbox. 跨平台哔哩哔哩工具箱,支持下载视频、番剧等等各类资源 项目地址: https://gitcode.com/GitHub_Trending/bilit/BiliTools …

2026/7/5 0:03:34 阅读更多 →
威胁模型全解析:从新手入门到实战应用,助你构建安全产品!

威胁模型全解析:从新手入门到实战应用,助你构建安全产品!

威胁模型的陌生现状在忙碌疲惫的一天里,参与了关于混合后量子密码学的讨论,应付端点攻击找茬的人,还参与留言板讨论后,发现“威胁模型”对多数人仍是陌生概念,且多被当作时髦用语。有趣的相关画作有一幅由 Embyr 创作的…

2026/7/5 0:03:34 阅读更多 →
渗透测试入门指南:从零基础到实战环境搭建

渗透测试入门指南:从零基础到实战环境搭建

1. 从“看热闹”到“入门”:我理解的渗透测试到底是什么?每次看到新闻里说某个大公司的数据被“黑”了,或者某个网站被攻击导致服务瘫痪,你是不是和我一样,心里会冒出两个念头:一是“这黑客真厉害”&#x…

2026/7/5 0:07:38 阅读更多 →

月新闻