Transaction rolled back because it has been marked as rollback-only

现象
日志里提示了如下一个错误:
Transaction rolled back because it has been marked as rollback
意思是,准备提交事务,将落地的数据库数据生效时,事务已经被设置为rollback-only了,无法提交事务。后面仔细看了一下代码,原来是开了事务的方法里,抛了一个异常,但是程序把这个异常给catch住了,没往上抛。

原因
字面上的意思就是:事务已回滚,因为它已被标记为仅回滚,那为什么会标记为仅回滚呢?

其实原因就是嵌套事务导致的,因为spring事务有传递性,spring默认的事务传播级别是PROPAGATION_REQUIRED,即当前上下文存在事务则用此事务,如果不存在事务则新建一个事务执行;

那么现在有A和B两个方法,这两个方法都开启了事务,A方法中调用B方法(因为都使用事务,默认的事务传播级别是PROPAGATION_REQUIRED,所以这过程中会使用同一个事务);

当执行B方法的时候,B方法抛出异常,这个时候事务就会被标记为仅回滚(因为在B方法中抛出异常,B方法这事务本该是要回滚,所以会将B方法的事务标记为rollback-only);

但是在A方法又catch到B方法抛的异常,但是A方法catch到异常后没有继续往上抛出,而是继续执行后面的代码,最后正常提交事务,那么就会抛出 Transaction rolled back because it has been marked as rollback-only这异常!(因为AB是用同一个事务,在B方法执行的时候这个事务就标记为rollback-only,然后A方法继续使用该事务,然后又执行事务提交的操作,所以最后会抛异常)

1
2
3
4
5
6
7
8
9
@Transactional
@Override
public boolean A(User user) {
try {
userMapper.B(user);
} case {
//这里做了保存,没有继续向上抛错
}
}
1
2
3
4
5
6
7
@Transactional
@Override
public boolean B(User user) {
//.....
int i = 1/0; //这里报错了
return false;
}

解决办法
1、在B方法中进行try case case中不进行抛错处理;
2、在A方法case中继续抛错处理;
3、在B方法上的Transaction 添加事务传播规则为 propagation = Propagation.REQUIRES_NEW 新建事务进行处理