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

问题出现

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

问题排查

通过排查日志发现,具体的错误信息是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)
public void methodA(){
	System.out.println(1 / 0);
}

@Transactional(rollbackFor = Exception.class, propagation = Propagation.REQUIRES_NEW)
public void methodB(){
	try{
		insert();
		methodA();
	}catch(Exception e){
		System.out.println("有异常");
	}
}

你可能感兴趣的:(事务,事务传播)