问题描述

事务设置手动回滚:TransactionAspectSupport.currentTransactionStatus().setRollbackOnly()

代码需要返回比较友好的提示,但try…catch了,事务就不会回滚了,所以在catch中设置手动回滚,但每次执行完就抛出异常

2023-01-08 13:37:51.790 ERROR 5192 --- [nio-8880-exec-1] o.a.c.c.C.[.[.[.[dispatcherServlet] : Servlet.service() for servlet [dispatcherServlet] in context with path [/ceshi] threw exception [Request processing failed; nested exception is org.springframework.transaction.UnexpectedRollbackException: Transaction rolled back because it has been marked as rollback-only] with root cause

org.springframework.transaction.UnexpectedRollbackException: Transaction rolled back because it has been marked as rollback-only

at org.springframework.transaction.support.AbstractPlatformTransactionManager.processRollback(AbstractPlatformTransactionManager.java:870) ~[spring-tx-5.2.8.RELEASE.jar:5.2.8.RELEASE]

at org.springframework.transaction.support.AbstractPlatformTransactionManager.commit(AbstractPlatformTransactionManager.java:707) ~[spring-tx-5.2.8.RELEASE.jar:5.2.8.RELEASE]

at org.springframework.transaction.interceptor.TransactionAspectSupport.commitTransactionAfterReturning(TransactionAspectSupport.java:633) ~[spring-tx-5.2.8.RELEASE.jar:5.2.8.RELEASE]

at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:386) ~[spring-tx-5.2.8.RELEASE.jar:5.2.8.RELEASE]

at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:118) ~[spring-tx-5.2.8.RELEASE.jar:5.2.8.RELEASE]

at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186) ~[spring-aop-5.2.8.RELEASE.jar:5.2.8.RELEASE]

at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:749) ~[spring-aop-5.2.8.RELEASE.jar:5.2.8.RELEASE]

at org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:95) ~[spring-aop-5.2.8.RELEASE.jar:5.2.8.RELEASE]

at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186) ~[spring-aop-5.2.8.RELEASE.jar:5.2.8.RELEASE]

at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:749) ~[spring-aop-5.2.8.RELEASE.jar:5.2.8.RELEASE]

at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:691) ~[spring-aop-5.2.8.RELEASE.jar:5.2.8.RELEASE]

at cn.lw.service.impl.ShiwuServiceImpl$$EnhancerBySpringCGLIB$$33a86b07.insertRobllBack() ~[classes/:na]

原因分析:

异常示例代码如下:

@Transactional

public String insertRobllBack(ForlanA forlanA, ForlanB forlanB) {

return forlanaService.insertForlanA(forlanA);

}

@Transactional

public String insertForlanA(ForlanA forlanA) {

try {

forlanBService.insertForlanB(new ForlanB());

} catch (Exception e) {

e.printStackTrace();

// 为了支持特定异常,设置手动回滚

TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();

return "特定异常结果";

}

return "成功";

}

@Transactional

public void insertForlanB(ForlanB forlanB) {

forlanBDao.insert(forlanB);

int res = 1 / 0; //java.lang.ArithmeticException: / by zero

}

分析核心源码AbstractPlatformTransactionManager

@Override

public final void commit(TransactionStatus status) throws TransactionException {

if (status.isCompleted()) {

throw new IllegalTransactionStateException(

"Transaction is already completed - do not call commit or rollback more than once per transaction");

}

DefaultTransactionStatus defStatus = (DefaultTransactionStatus) status;

if (defStatus.isLocalRollbackOnly()) {

if (defStatus.isDebug()) {

logger.debug("Transactional code has requested rollback");

}

processRollback(defStatus, false);

return;

}

if (!shouldCommitOnGlobalRollbackOnly() && defStatus.isGlobalRollbackOnly()) {

if (defStatus.isDebug()) {

logger.debug("Global transaction is marked as rollback-only but transactional code requested commit");

}

processRollback(defStatus, true);

return;

}

processCommit(defStatus);

}

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);

}

}

通过源码,我们发现如果有全局回滚标记,还进行commit的情况下,则引发UnexpectedRollbackException

if (!shouldCommitOnGlobalRollbackOnly() && defStatus.isGlobalRollbackOnly()) {

if (defStatus.isDebug()) {

logger.debug("Global transaction is marked as rollback-only but transactional code requested commit");

}

processRollback(defStatus, true);

return;

}

// 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");

}

If a participating transaction (e.g. with PROPAGATION_REQUIRED or PROPAGATION_SUPPORTS encountering an existing transaction) fails, the transaction will be globally marked as rollback-only

原因:就是我们内部事务已经抛出异常了,此时,事务已经被标记为rollback-only,最外层事务commit的话,这就有问题了

解决方案:

1、修改内层事务传播特性为@Transactional(propagation = Propagation.REQUIRES_NEW) ,这样内层和外层分别使用了不同事务,就不影响了2、try…catch写到最外层事务,但是记得在catch设置手动回滚,不然还是存在上面问题

添加链跨方法的异常捕获,是会回滚的描述

3、不要try…catch,异常直接抛到最外层

查看原文