事务注解在哪些情况下失效?
未标记
考查重点:代理生效条件与回滚语义。检查是否经 Spring 代理、回滚异常是否匹配、数据库操作是否在同一事务资源内;自调用和吞异常是常见原因
本题目录
参考答案
核心回答
以 Spring Framework 6.2、默认代理模式、普通 JDBC/JPA 本地事务为例,事务注解“失效”通常要分成三类:调用没经过事务增强、回滚条件没满足、操作不在这笔事务的资源范围内。
常见场景有:
- 没有通过
Spring的事务代理调用:对象由自己new出来、未启用事务基础设施,或者同一个对象内部直接调用另一个事务方法。自调用绕过代理,被调用方法上的事务属性不会得到这次增强。 - 方法不能被当前代理正确拦截:接口代理要求事务方法是代理接口中定义的
public方法;Spring6 的类代理默认还能支持protected、包可见方法,但private方法无法这样增强,final方法也不能被类代理重写增强。不能把“非public都失效”套到所有版本和代理类型。 - 异常与回滚规则不匹配:默认规则对
RuntimeException、Error回滚,受检异常默认不回滚;异常被内部捕获并正常返回,拦截器也可能看不到失败。还要检查rollbackFor、noRollbackFor和全局默认规则。 - 线程或资源边界变了:新线程、异步任务不会自动继承调用线程的本地事务;多数据源时选错事务管理器,或数据库访问没有使用其管理的资源,也不会获得预期的整体回滚。
- 传播行为与预期不同:例如
SUPPORTS在没有外层事务时可以不创建事务,NOT_SUPPORTED会挂起已有事务。这是配置语义,不能一律当成注解故障。
自调用为什么最容易误判?
假设 placeOrder 直接调用同一对象的 saveOrder,只有后者标了 @Transactional。调用走的是目标对象的 this,不会重新经过代理,因此不会因后者的注解开启事务。更清楚的做法是把完整的事务工作放到独立服务,由调用方通过注入的代理进入,或明确使用编程式事务边界。
但如果 placeOrder 已经通过代理开启事务,saveOrder 使用同一受管理资源时,数据库操作仍可能参与外层事务。此时失去的是 saveOrder 上单独声明的事务语义,例如它声明的 REQUIRES_NEW 不会通过这次自调用生效,不能说里面所有 SQL 必然没有事务。
代理还需要完成初始化,因此不应依赖同一 Bean 的 @PostConstruct 调用触发事务增强。AspectJ 字节码织入模式则不受同样的自调用代理限制,需要与这里讨论的默认模式分开。
为什么抛异常了仍可能不回滚?
先看异常是否越过事务拦截边界,再看匹配到的回滚规则。
默认配置下,业务抛出受检异常,不会仅因为“抛了 Exception”就自动回滚。可以按业务语义配置具体的 rollbackFor;Spring 6.2 也支持把全局默认回滚行为改成 ALL_EXCEPTIONS。因此排查时要看实际配置,不能只背默认值。
如果方法 catch 住异常、返回失败码,且事务没有被标记为 rollback-only,事务可能正常提交。反过来,若内层已把共同事务标为 rollback-only,外层吞掉异常也不代表可以提交,提交时可能收到 UnexpectedRollbackException。catch 与“不回滚”没有无条件对应关系。
线程和事务管理器要怎么查?
普通 PlatformTransactionManager 事务主要绑定当前执行线程。把一部分写入丢给线程池后,原线程回滚不会自动撤销新线程的写入;新线程若通过合适的事务代理进入,可以开启自己的事务,但那是另一笔事务。响应式事务使用 Reactor 上下文,不能直接套用线程绑定的解释。
多数据源场景要确认 transactionManager 指定的管理器、实际执行 SQL 的数据源和连接是否对应。一个普通本地事务也不会自动覆盖另一数据库、HTTP 调用或消息发送。
实际定位可以沿着“调用引用与代理→方法属性→异常经过的边界→所用线程和数据资源”检查,再查看事务开启、参与、提交或回滚日志。验证回滚时,应从事务结束后的独立查询确认数据结果;只看方法返回失败或日志打印异常并不够。
参考资料
- 国内:JavaGuide:Spring 事务详解:采用事务代理、自调用和注解参数部分,解释事务增强在哪个调用边界发生,以及 rollbackFor、传播行为为什么会影响结果。
- 国内:码力全开:全面解析 Spring 事务的失效以及如何避免:采用自调用、事务管理器和 Bean 创建场景,解释正文中注解存在却未经过事务基础设施的几类原因。
- 国内:美团技术团队·明辉:缓存那些事:借鉴 Spring AOP 代理与 this 内部调用的解释,说明正文中自调用绕过代理、事务属性无法获得本次增强的原因。
- 国外:Spring Framework 6.2:Using @Transactional:采用代理模式、方法可见性、多事务管理器和 6.2 默认回滚配置,明确这些行为各自的版本与配置条件。
- 国外:Spring Framework 6.2:Rolling Back a Declarative Transaction:采用异常到达事务基础设施后的回滚判断、规则匹配和 rollback-only 说明,解释“有异常”为什么不必然等于按预期回滚。
相关面经
6 篇华盛通
Java实习生@Transactional 什么情况下会失效?
极兔速递
后端开发工程师事务注解在什么情况下会失效吗?
蔚来
Java 开发工程师Spring 声明式事务失效的场景
禅游科技
全栈开发工程师问redis实现的必要性?说用redis和aop不如直接nacos等,然后转到相关八股,aop实现基于的动态代理,什么情况会aop失效,然后说和事务注解相似,又问我事务失效的情况
亚信科技
Java 开发工程师Java基础:ArrayList、LinkedList、Bean生命周期、Spring事务失效、自动装配、JVM、测试注解等基础知识点
展开剩余 1 篇面经
上海景行智合安全技术
Java 开发工程师Spring @Transactional 事务注解失效有哪些场景
