Spring @Transactional 没回滚?按代理调用、异常和传播行为排查

代码上写了 @Transactional,数据库却留下了本应撤销的数据。排查时先确认事务有没有生效,再确认异常是否触发回滚,最后看实际操作是否属于同一个事务。

先查调用有没有经过代理

Spring 默认代理模式会拦截从外部经过代理进入的方法调用。同一个对象内部直接调用另一个带注解的方法,不会因为那个方法上的注解而自动新增事务拦截。

假设 createOrder 在类内部直接调用 saveOrder,只有 saveOrder 标了注解。需要检查入口是否通过代理,以及当前是否已经有其他事务。看到注解不能推断这一次调用受到了它的管理。Spring 官方说明明确说明了自调用的限制。

自己 new 出来的对象,同样需要检查是否脱离容器管理。方法可见性还受代理类型与版本影响。Spring 6 的类代理可支持部分非 public 方法,接口代理有其要求,不能把"所有非 public 方法都失效"当成通用结论。

异常有没有传到事务拦截器

传统默认规则下,RuntimeException 与 Error 触发回滚,普通受检异常不会自动触发。可以按业务设置 rollbackFor。Spring 6.2 起还支持调整全局默认回滚策略,因此需要查看当前项目的真实配置。

假设数据库操作之后抛出了异常,但方法内部 catch 住并正常返回。代理可能看不到应触发回滚的异常。仅打印日志,无法表达业务希望回滚的决定。

处理方式应与业务语义一致,需要回滚就通过合适的异常或回滚标记表达,同时避免把应该正常处理的业务状态全部转成异常。

传播行为决定共享哪些事务

REQUIRED 在已有事务中通常参与同一个物理事务。内部参与者设置 rollback-only 后,外层即使捕获异常继续执行,也可能在最终提交时收到 UnexpectedRollbackException。

REQUIRES_NEW 使用独立事务。内层已提交的记录,不会因为外层随后回滚而自动撤销。外层占着连接再申请内层连接,还需要考虑连接池容量与并发压力。

NESTED 通常依赖保存点以及事务管理器和资源的支持。不能仅凭注解名称,就认为所有数据访问技术都能按同样方式回滚内层操作。

这些规则可以进一步核对传播行为文档。

本地事务管理哪些资源

数据库回滚不会撤回已经发送的邮件,也不一定取消外部 HTTP 请求。如果订单事务里调用了外部支付接口,数据库失败以后,外部扣款结果必须单独核对。

这类流程需要明确幂等、状态核对与补偿安排。把全部调用放在带注解的方法中,不会自动让它们成为一个跨系统原子操作。

验证时通过真实服务入口调用,准备异常发生在写入前后、异常被捕获、独立内层提交等场景,再检查最终数据库状态。测试自身包裹的事务可能影响观察,需要确认验证的是生产调用方式。

沿着调用入口、异常处理和资源范围逐步检查,通常比反复增删注解更容易找到原因。

我是程序员Sunday,本文相关的完整解析收录在 sunday面试指南,可继续阅读《Spring 的 @Transactional 为什么会失效?事务传播行为怎么选?》。

相关推荐
文丑颜不良啊5 分钟前
LeetCode1401~1420题目解析
java·leetcode
Nuanyt7 分钟前
LeetCode Hot 100 链表专题2 3 题 (Java)
java·笔记·算法·leetcode·链表
文丑颜不良啊14 分钟前
LeetCode1341~1360题目解析
java·leetcode
系统集成-鹰翔40 分钟前
ERP推单数据库怎么备份:SQLite WAL模式下只复制db文件为什么可能漏任务
javascript·数据库·经验分享·后端
QianhangQianping41 分钟前
撤销重做不是「存快照」:一个可视化编辑器的Operation 设计
java·前端·数据库·typescript·编辑器·前端架构·撤销重做
海绵宝宝转agent1 小时前
LeetCodehot100题解
java·笔记·学习·算法·leetcode·深度优先
SunnyDays10111 小时前
Java 操作 Word 脚注和尾注:添加、读取与删除
java·word
SunnyDays10111 小时前
Java 为 Word 添加页码:隐藏封面页码、从指定页开始编号、奇偶页页码设置等
java·word·页码
开源推荐官1 小时前
多门店 O2O 商城选型复盘:从开发视角对比主流开源系统
java·架构·开源·php
吴名氏.1 小时前
Java转AI Agent教程(第二周)超时重试与错误分类
java·人工智能·python·分类·ai agent