spring事务异常---Transaction rolled back because it has been marked as rollback-only

本文探讨了在Spring框架中,事务嵌套与异常处理不当导致的Transactionrolledbackbecauseithasbeenmarkedasrollback-only错误。分析了错误产生的原因,提出了两种解决方案:一是确保内层事务异常时整体事务回滚;二是修改内层事务传播方式。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

问题出现

最近在重构之前同事的代码,完成重构后对代码进行调试的过程中,出现了服务调用异常的错误,而不是在项目中我们自定义的异常错误信息,所以怀疑这是框架层面的错误,而非业务代码上的错误。

问题排查

通过排查日志发现,具体的错误信息是Transaction rolled back because it has been marked as rollback-only,很明显是一个和事务相关的错误。且通过对业务逻辑中日志答应状况的排查,发现,业务正确执行到了最后,但是最后进行事务提交的时候进行了错误。开启spring事务后,当代码正确执行时,spring会在事务的最后进行一次事务提交,说明这是一个事务提交时的错误。

问题分析

通过对代码的查阅发现改业务逻辑上存在事务嵌套的情况。
通过对异常栈的仔细排查,发现,内层事务在一定场景下抛出了异常,但是却被外层事务用try–catch接住了。于是在内层事务异常的情况下,外层事务继续执行了。

原因解释

当spring开启事务时,如果不修改propagation的参数,则默认是propagation.REQUIRED。即如果有没有事务则新启一个事务,如果已经存在事务则加入这个事务。
当内层事务异常的情况下,如果是这种传播方式,正常来讲是需要回滚的,但是spring知识给内层事务做了一个rollback的标记。所以当内层事务抛出的异常被外层try-----catch时,外层事务正常执行,但在最后提交的时候发现,内层被标记了rollbck,所以就会抛出Transaction rolled back because it has been marked as rollback-only这个异常信息。

解决方案

原场景,内层事务的异常被外层事务捕获,内层被标记rollback,而外层提交,最后事务提交校验时抛出Transaction rolled back because it has been marked as rollback-only异常

@Transactional(rollbackFor = Exception.class)
public void methodA(){
	insert();
	System.out.println(1 / 0);
}

@Transactional(rollbackFor = Exception.class)
public void methodB(){
	try{
		methodA();
	}catch(Exception e){
		System.out.println("有异常");
	}
}
可以分两种情况来解决这个异常
  • 如果我们需要内层异常的情况下,回滚整个事务,可以让内层事务抛出的异常被外层事务的try----catch处理,再抛出新的异常,或者外层不通过try—catch处理这个异常。
  • 当然如果内层事务没有复用过,只是在这个地方使用,直接把内层的事务去了,让他和外层合并成一个事务也能解决这个问题。
@Transactional(rollbackFor = Exception.class)
public void methodA(){
	System.out.println(1 / 0);
}

@Transactional(rollbackFor = Exception.class)
public void methodB(){
	try{
		insert();
		methodA();
	}catch(Exception e){
		throw new Exception("存在异常")
	}
}
  • 如果内层事务异常的情况下只回滚内层事务,修改内层事务的事务传播方式
@Transactional(rollbackFor = Exception.class, propagation = Propagation.REQUIRES_NEW)
public void methodA(){
	System.out.println(1 / 0);
}

@Transactional(rollbackFor = Exception.class)
public void methodB(){
	try{
		insert();
		methodA();
	}catch(Exception e){
		System.out.println("有异常");
	}
}
<think>好的,我现在需要帮助用户解决关于“Transaction rolled back because it has been marked as rollback-only异常的问题。首先,我得回顾一下用户提供的引用资料,整理出可能的原因和解决方案。 根据引用[1],这个异常Spring事务的传播行为有关,特别是REQUIRED、REQUIRES_NEW和NESTED的区别。可能当内层事务回滚时,外层事务被标记为rollback-only,导致提交时整体回滚。引用[2]提到,当内层事务抛出异常并被捕获后,外层事务在提交时检测到需要回滚,从而抛出异常。这说明异常处理不当可能是原因之一。 引用[3]指出该异常常发生在多业务场景中使用try-catch,导致事务嵌套混乱。解决方法是使用NESTED传播模式,让内层事务作为外层事务的嵌套子事务,这样内层的回滚不会强制外层回滚。引用[4]则建议避免随意捕获异常,确保只有能处理的异常才捕获,否则应抛出RuntimeException。 接下来,我需要将这些信息组织成结构清晰的回答,分点说明原因和解决方案。要注意使用中文,正确应用LaTeX格式,比如事务传播行为的类型可能需要用行内公式,如`REQUIRED`。同时,生成的相关问题要符合用户需求,可能涉及事务传播行为、异常处理、具体应用场景等。 需要确保回答正确引用每个来源,比如在解释原因时引用[^1][^2],解决方案引用[^3][^4]。另外,用户可能对事务传播行为的具体差异感兴趣,所以相关问题可以包括不同传播类型的区别、如何选择合适的事务传播行为等。 最后,检查是否符合系统级指令,如行内数学表达式是否正确使用$...$,独立公式是否用$$...$$,避免任何格式错误。同时确保回答真实可靠,引用来源准确。</think>### Transaction rolled back because it has been marked as rollback-only异常解析 #### 异常原因 1. **事务传播行为冲突** 当内层事务(如使用`REQUIRED`传播行为)抛出异常并被标记为回滚时,外层事务会被标记为`rollback-only`。此时即使外层事务尝试提交,也会因该标记强制回滚,导致异常。 2. **不恰当的异常捕获** 若内层事务异常被`try-catch`捕获但未重新抛出`RuntimeException`,外层事务无法感知异常,继续提交时会触发回滚。 3. **事务嵌套层级混乱** 使用默认的`REQUIRED`传播行为时,内外层事务共享同一事务上下文。若内层事务回滚,外层事务无法独立提交,导致矛盾。 #### 解决方案 1. **调整事务传播行为** - 使用`NESTED`传播模式:允许内层事务作为嵌套子事务独立回滚,不影响外层事务- 使用`REQUIRES_NEW`传播模式:创建新事务,完全隔离内外层事务(需注意锁和性能问题)。 2. **规范异常处理** - 仅捕获可处理的异常,否则重新抛出`RuntimeException`或其子类。 - 示例代码: ```java try { innerTransaction(); } catch (BusinessException e) { // 明确可处理的异常 // 处理逻辑 } catch (Exception e) { throw new RuntimeException(e); // 强制触发回滚 } ``` 3. **事务范围最小化** 避免在包含多个业务操作的代码块上使用事务,改为对原子操作单独控制事务--- $$ \text{事务传播行为选择公式} = \begin{cases} \text{REQUIRED} & \text{默认共享事务} \\ \text{NESTED} & \text{嵌套子事务} \\ \text{REQUIRES\_NEW} & \text{独立事务} \end{cases} $$ ---
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值