1. 事务回滚标记的典型场景剖析
第一次看到"Transaction rolled back because it has been marked as rollback-only"这个错误时,很多开发者都会感到困惑。明明已经用try-catch捕获了所有异常,为什么事务还是回滚了?这就像是你以为已经关掉了水龙头,但地下室还是被淹了——肯定有什么关键机制被你忽略了。
在实际项目中,这种情况通常出现在嵌套事务的场景中。比如你在用户注册的主业务流程里调用了发送邮件的子方法,两个方法都加了@Transactional注解。当发送邮件失败时,即使你在主方法里捕获了这个异常,整个注册流程的事务依然会回滚。这是因为Spring的事务管理器在底层维护了一个"rollback-only"的状态标记,一旦这个标记被设置,就像打开了单向阀门,事务只能向回滚的方向流动。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. Spring事务状态机的运作原理
2.1 事务传播机制的核心设计
Spring的事务管理本质上是一个状态机,REQUIRED(默认)传播级别下,嵌套的@Transactional方法会共享同一个物理事务。当内层方法抛出异常时,事务管理器会做两件事:首先将异常转换为RollbackRuleAttribute匹配的规则,然后将事务标记为rollback-only。这个标记就像交通信号灯,一旦变红就无法再变绿。
我曾在电商项目中遇到过这样的案例:订单服务调用库存服务时,库存服务抛出了异常但被订单服务捕获。理论上订单应该能创建成功,但实际上整个事务都回滚了。通过调试发现,在DefaultTransactionStatus类中有个isRollbackOnly标志位,这个标志位一旦被设置为true就无法再修改。
2.2 事务状态的生命周期
理解事务状态流转对解决这类问题至关重要。一个Spring事务的生命周期大致如下:
- 事务开启时创建TransactionStatus实例
- 执行过程中遇到异常会检查回滚规则
- 匹配到回滚规则则设置rollback-only=true
- 提交时检查rollback-only标志位
- 如果为true则强制回滚并抛出异常
关键点在于:rollback-only标志位的设置是不可逆的。就像烧断的保险丝,即使你修好了电路,保险丝也不会
