一、问题描述
事务的注解使用起来很简单,但是如果只了解皮毛就会出现事务失效、事务异常等问题。
关于事务失效场景,我在这篇《为什么你的事务不好使?一下解决Java中N种事务失效场景》文章中记录了多种失效场景,这里就不再赘述。
本次主要讲,在事务嵌套(加了事务的方法,调用加了事务的方法)时,报错
Transaction rolled back because it has been marked as rollback-only, org.springframework.transaction.UnexpectedRollbackException: Transaction rolled back because it has been marked as rollback-only
二、问题产生原因
2.1、问题复现
如下,在ClassA类中有个加了事务的A方法,调用了ClassB中的加了事务的B方法
public Class ClassA {
private ClassB classB;
@Transactional
public void A() {
try {
B();
} catch (Exception e) {
log.error("啥也不干");
}
}
}
public Class ClassB {
@Transactional
public void B() {
throw new Exception();
}
}
这种情况下就会报错:Transaction rolled back because it has been marked as rollback-only
2.2、报错原因
当A方法的事物(REQUIRED),B方法的事物(REQUIRED),A调用B方法,在spring中,spring将会把这些事务合二为一。
当整个方法中每个子方法没报错时,整个方法执行完才提交事务。
如果某个子方法有异常,spring将该事务标志为rollback only。如果这个子方法没有将异常往上抛,或者主父方法将子方法抛出的异常捕获了,那么,该异常就不会触发事务进行回滚,事务就会在整个方法执行完后就会提交,这时就会造成Transaction rolled back because it has been marked as rollback-only的异常。(由于异常被标记了rollback only,但是又执行了commit,此时就会报这个错
)
三、解决方法
-
方法1:父方法不要捕获异常
在2.1的举例中,A方法去掉try…catch即可
-
方法2:子方法的事务propagation属性换为NESTED
在2.1的举例中,将B方法的事务注解的属性改为NESTED
public Class ClassB { @Transactional(propagation = Propagation.NESTED) public void B() { throw new Exception(); } }
属性 | 功能描述 |
---|---|
Propagation.NESTED | 表示如果当前已经存在一个事务,那么该方法将会在嵌套事务中运行。嵌套的事务可以独立于当前事务进行单独地提交或回滚。如果当前事务不存在,那么其行为和 Propagation.REQUIRED 效果一样。 |
「喜欢这篇文章,您的关注和赞赏是给作者最好的鼓励」
关注作者
【版权声明】本文为墨天轮用户原创内容,转载时必须标注文章的来源(墨天轮),文章链接,文章作者等基本信息,否则作者和墨天轮有权追究责任。如果您发现墨天轮中有涉嫌抄袭或者侵权的内容,欢迎发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。