存在一个A方法(带有事物REQUIRED),B方法也存在事物REQUIRED,A方法调用B方法,担心B方法中会报错,就进行了try(){}处理,也没有往上层去抛,这个时候执行结果是什么
重现问题走到事故现场,重现那段惨案。
A方法调用了B方法,A和B都存在事物,并且在A调用的时候把B进行了try(){}操作
B方法:
测试类登场:
01
运行结果程序报错,数据也没有插入数据库。
按照我的想法addCustMegreWaitB()出现了错误,但是我们在addCustMegreWaitA()方法中进行了捕获也即是try()catch{},预想程序会成功执行,只不过addCustMegreWaitB()方法中的数据插入不到数据库,但是addCustMegreWaitA()是可以的。
还是我太天真与年轻,真相确不是这样。。。。残酷的现实狠狠的给我了一记耳光。
数据库:
查看日志文件:
Transaction rolled back because it has been marked as rollback-only,中文翻译为:事务已回滚,因为它被标记成了只回滚
原因分析出现了问题,就要分析问题,然后解决问题。
在addCustMegreWaitA方法准备提交的时候,transaction已经被addCustMegreWaitB方法设置为rollback-only了,但是addCustMegreWaitA方法中将addCustMegreWaitB的报错将给抓住消化了,没有继续向外抛出,所以addCustMegreWaitA结束的时候,transaction会执commit操作,所以就报错了。看看处理回滚的源码:
private void processRollback(DefaultTransactionStatus status, boolean unexpected) {
try {
boolean unexpectedRollback = unexpected;
try {
triggerBeforeCompletion(status);
if (status.hasSavepoint()) {
if (status.isDebug()) {
logger.debug("Rolling back transaction to savepoint");
}
status.rollbackToHeldSavepoint();
}
else if (status.isNewTransaction()) {
if (status.isDebug()) {
logger.debug("Initiating transaction rollback");
}
doRollback(status);
}
else {
// Participating in larger transaction
if (status.hasTransaction()) {
if (status.isLocalRollbackOnly() || isGlobalRollbackOnParticipationFailure()) {
if (status.isDebug()) {
logger.debug("Participating transaction failed - marking existing transaction as rollback-only");
}
doSetRollbackOnly(status);
}
else {
if (status.isDebug()) {
logger.debug("Participating transaction failed - letting transaction originator decide on rollback");
}
}
}
else {
logger.debug("Should roll back transaction but cannot - no transaction available");
}
// Unexpected rollback only matters here if we're asked to fail early
if (!isFailEarlyOnGlobalRollbackOnly()) {
unexpectedRollback = false;
}
}
} catch (RuntimeException | Error ex) {
triggerAfterCompletion(status, TransactionSynchronization.STATUS_UNKNOWN);
throw ex;
}
triggerAfterCompletion(status, TransactionSynchronization.STATUS_ROLLED_BACK);
// Raise UnexpectedRollbackException if we had a global rollback-only marker
if (unexpectedRollback) {
throw new UnexpectedRollbackException(
"Transaction rolled back because it has been marked as rollback-only");
}
}
finally {
cleanupAfterCompletion(status);
}
}
复制代码
简单分析:addCustMegreWaitB()有事务,然后处理的时候有这么一句:
这个时候把参数unexpectedRollback置为false了,所以当addCustMegreWaitA()事务需要提交的时候
所以,就之前抛出异常了。
当A方法的事物(REQUIRED),B方法的事物(REQUIRED),A调用B方法,在spring中,spring将会把这些事务合二为一。当整个方法中每个子方法没报错时,整个方法执行完才提交事务,
如果某个子方法有异常,spring将该事务标志为rollback only。如果这个子方法没有将异常往上整个方法抛出或整个方法未往上抛出,那么该异常就不会触发事务进行回滚,事务就会在整个方法执行完后就会提交,这时就会造成Transaction rolled back because it has been marked as rollback-only的异常。
如何解决这个问题1.将这个addCustMegreWaitB()中的错误try(){}后继续往上层去抛,交给上层去处理掉,不能内部消化
2.将addCustMegreWaitB()中的事物改为(REQUIRES_NEW)
代码验证一下这种方式:
运行结果:
程序执行正常。数据库插入了一条数据,addCustMegreWaitB()方法中进行 了回滚。
加入51码农网。www.51manong.com 寻找一起进步的人,在编程的道路上从此不孤单。
关注公众号:51码农网