之前经常报"Transaction rolled back because it has been marked as rollback-only"这个异常
字面意思是"事务回滚了,因为它被标记了必须回滚",最开始完全不懂事务的嵌套,每次出现这个错误都想知道为什么,但是总是不能重现,后面反复折腾终于弄明白了怎么回事。
之前不能重现的一个重要原因是:同一个类,内部方法调用不走代理,spring基于注解的事务是基于代理的,不走代理,被调用的方法就不受事务管理代码的控制,自然无法重现问题.
测试代码:
TestController
-
@Autowired -
TestRollbackService testRollbackService; -
@RequestMapping("/test1") -
public void test1(){ -
try{ -
testRollbackService.test1(); -
}catch(Exception e){ -
e.printStackTrace(); -
} -
}
TestRollbackServiceImpl
-
@Autowired -
StudentMapper studentMapper; -
@Autowired -
TestTransactionService testTransactionService; -
@Transactional(rollbackFor = Exception.class) -
public void test1(){ -
Student studentSelect = new Student(); -
studentSelect.setId(new Long(1)); -
Student student = studentMapper.selectByPrimaryKey(studentSelect); -
try{ -
testTransactionService.test2(); -
}catch(Exception e){ -
e.printStackTrace(); -
} -
}

TestTransactionServiceImpl
-
@Autowired -
StudentMapper studentMapper; -
@Transactional(rollbackFor = Exception.class) -
public void test2(){ -
Student studentForInsert = new Student(); -
studentForInsert.setId(new Long(19)); -
studentForInsert.setName("测试11"); -
studentForInsert.setScore(new BigDecimal(69)); -
studentMapper.updateByPrimaryKey(studentForInsert); -
System.out.println(1/0); -
}
TestRollbackService.test1(方法A)中调用了TestTransactionService.test2(方法B),上述代码可以触发回滚异常的报错
两个方法都加了事务注解,并且两个方法都会受到到事务管理的拦截器增强,并且事务传播的方式都是默认的,也就是REQUIRED,当已经存在事务的时候就加入事务,没有就创建事务。这里A和B都受事务控制,并且是处于同一个事务的。
A调用B,A中抓了B的异常,当B发生异常的时候,B的操作应该回滚,但是A吃了异常,A方法中没有产生异常,所以A的操作又应该提交,二者是相互矛盾的。
spring的事务关联拦截器在抓到B的异常后就会标记rollback-only为true,当A执行完准备提交后,发现rollback-only为true,也会回滚,并抛出异常告诉调用者。
程序时序图如下:

1.程序入口
程序入口肯定是代理类,这里走是cglib的代理
入口方法:
org.springframework.aop.framework.CglibAopProxy.DynamicAdvisedInterceptor#intercept
该方法中
retVal = (new CglibAopProxy.CglibMethodInvocation(proxy, target, method, args, targetClass, chain, methodProxy)).proceed();
继续调用
org.springframework.aop.framework.ReflectiveMethodInvocation#proceed
然后到了事务管理的拦截器
org.springframework.transaction.interceptor.TransactionInterceptor#invoke
-
@Nullable -
public Object invoke(MethodInvocation invocation) throws Throwable { -
Class<?> targetClass = invocation.getThis() != null ? AopUtils.getTargetClass(invocation.getThis()) : null; -
Method var10001 = invocation.getMethod(); -
invocation.getClass(); -
return this.invokeWithinTransaction(var10001, targetClass, invocation::proceed); -
}
2.invokeWithinTransaction(事务管理的主方法)
-
@Nullable -
protected Object invokeWithinTransaction(Method method, @Nullable Class<?> targetClass, TransactionAspectSupport.InvocationCallback invocation) throws Throwable { -
TransactionAttributeSource tas = this.getTransactionAttributeSource(); -
TransactionAttribute txAttr = tas != null ? tas.getTransactionAttribute(method, targetClass) : null; -
PlatformTransactionManager tm = this.determineTransactionManager(txAttr); -
String joinpointIdentification = this.methodIdentification(method, targetClass, txAttr); -
Object result; -
if (txAttr != null && tm instanceof CallbackPreferringPlatformTransactionManager) { -
TransactionAspectSupport.ThrowableHolder throwableHolder = new TransactionAspectSupport.ThrowableHolder(); -
try { -
result = ((CallbackPreferringPlatformTransactionManager)tm).execute(txAttr, (status) -> { -
TransactionAspectSupport.TransactionInfo txInfo = this.prepareTransactionInfo(tm, txAttr, joinpointIdentification, status); -
Object var9; -
try { -
Object var8 = invocation.proceedWithInvocation(); -
return var8; -
} catch (Throwable var13) { -
if (txAttr.rollbackOn(var13)) { -
if (var13 instanceof RuntimeException) { -
throw (RuntimeException)var13; -
} -
throw new TransactionAspectSupport.ThrowableHolderException(var13); -
} -
throwableHolder.throwable = var13; -
var9 = null; -
} finally { -
this.cleanupTransactionInfo(txInfo); -
} -
return var9; -
}); -
if (throwableHolder.throwable != null) { -
throw throwableHolder.throwable; -
} else { -
return result; -
} -
} catch (TransactionAspectSupport.ThrowableHolderException var19) { -
throw var19.getCause(); -
} catch (TransactionSystemException var20) { -
if (throwableHolder.throwable != null) { -
this.logger.error("Application exception overridden by commit exception", throwableHolder.throwable); -
var20.initApplicationException(throwableHolder.throwable); -
} -
throw var20; -
} catch (Throwable var21) { -
if (throwableHolder.throwable != null) { -
this.logger.error("Application exception overridden by commit exception", throwableHolder.throwable); -
} -
throw var21; -
} -
} else { -
TransactionAspectSupport.TransactionInfo txInfo = this.createTransactionIfNecessary(tm, txAttr, joinpointIdentification); -
result = null; -
try { -
result = invocation.proceedWithInvocation(); -
} catch (Throwable var17) { -
this.completeTransactionAfterThrowing(txInfo, var17); -
throw var17; -
} finally { -
this.cleanupTransactionInfo(txInfo); -
} -
this.commitTransactionAfterReturning(txInfo); -
return result; -
} -
}

程序执行的是最后的else分支,步骤很清晰
1.获取 TransactionAttribute
2.基于TransactionAttribute获取TransactionManager
3.基于TransactionAttribute获取 joinpointIdentification(没研究什么作用)
4. 基于1,2,3创建的对象获取 TransactionAspectSupport.TransactionInfo,transactionInfo是TransactionAspectSupport的一个内部类
5.执行业务方法
6.抓到异常就回滚,并清除事务,然后向上抛异常;没有异常就清除事务,然后提交
对象之间的关联关系

3.各个对象的获取
TransactionManager的获取比较简单,程序里获取到的其实就是自己配置的bean
创建TransactionInfo的过程中要先获取TransactionStatus,TransactionStatus又需要拿到ConnectionHolder
3.1 createTransactionIfNecessary
org.springframework.transaction.interceptor.TransactionAspectSupport#createTransactionIfNecessary
-
protected TransactionAspectSupport.TransactionInfo createTransactionIfNecessary(@Nullable PlatformTransactionManager tm, @Nullable TransactionAttribute txAttr, final String joinpointIdentification) { -
if (txAttr != null && ((TransactionAttribute)txAttr).getName() == null) { -
txAttr = new DelegatingTransactionAttribute((TransactionAttribute)txAttr) { -
public String getName() { -
return joinpointIdentification; -
} -
}; -
} -
TransactionStatus status = null; -
if (txAttr != null) { -
if (tm != null) { -
status = tm.getTransaction((TransactionDefinition)txAttr); -
} else if (this.logger.isDebugEnabled()) { -
this.logger.debug("Skipping transactional joinpoint [" + joinpointIdentification + "] because no transaction manager has been configured"); -
} -
} -
return this.prepareTransactionInfo(tm, (TransactionAttribute)txAttr, joinpointIdentification, status); -
}

3.2 获取TransactionStatus
这里先调用 status = tm.getTransaction((TransactionDefinition)txAttr) 创建TransactionStatus
org.springframework.transaction.support.AbstractPlatformTransactionManager#getTransaction
-
public final TransactionStatus getTransaction(@Nullable TransactionDefinition definition) throws TransactionException { -
Object transaction = this.doGetTransaction(); -
boolean debugEnabled = this.logger.isDebugEnabled(); -
if (definition == null) { -
definition = new DefaultTransactionDefinition(); -
} -
if (this.isExistingTransaction(transaction)) { -
return this.handleExistingTransaction((TransactionDefinition)definition, transaction, debugEnabled); -
} else if (((TransactionDefinition)definition).getTimeout() < -1) { -
throw new InvalidTimeoutException("Invalid transaction timeout", ((TransactionDefinition)definition).getTimeout()); -
} else if (((TransactionDefinition)definition).getPropagationBehavior() == 2) { -
throw new IllegalTransactionStateException("No existing transaction found for transaction marked with propagation 'mandatory'"); -
} else if (((TransactionDefinition)definition).getPropagationBehavior() != 0 && ((TransactionDefinition)definition).getPropagationBehavior() != 3 && ((TransactionDefinition)definition).getPropagationBehavior() != 6) { -
if (((TransactionDefinition)definition).getIsolationLevel() != -1 && this.logger.isWarnEnabled()) { -
this.logger.warn("Custom isolation level specified but no actual transaction initiated; isolation level will effectively be ignored: " + definition); -
} -
boolean newSynchronization = this.getTransactionSynchronization() == 0; -
return this.prepareTransactionStatus((TransactionDefinition)definition, (Object)null, true, newSynchronization, debugEnabled, (Object)null); -
} else { -
AbstractPlatformTransactionManager.SuspendedResourcesHolder suspendedResources = this.suspend((Object)null); -
if (debugEnabled) { -
this.logger.debug("Creating new transaction with name [" + ((TransactionDefinition)definition).getName() + "]: " + definition); -
} -
try { -
boolean newSynchronization = this.getTransactionSynchronization() != 2; -
DefaultTransactionStatus status = this.newTransactionStatus((TransactionDefinition)definition, transaction, true, newSynchronization, debugEnabled, suspendedResources); -
this.doBegin(transaction, (TransactionDefinition)definition); -
this.prepareSynchronization(status, (TransactionDefinition)definition); -
return status; -
} catch (Error | RuntimeException var7) { -
this.resume((Object)null, suspendedResources); -
throw var7; -
} -
} -
}

3.3 获取transactionStatus前先获取DataSourceTransactionObject
程序最上面: Object transaction = this.doGetTransaction(); 创建DataSourceTransactionObject对象,这是DataSourceTransactionManager的内部类
org.springframework.jdbc.datasource.DataSourceTransactionManager#doGetTransaction
-
protected Object doGetTransaction() { -
DataSourceTransactionManager.DataSourceTransactionObject txObject = new DataSourceTransactionManager.DataSourceTransactionObject(); -
txObject.setSavepointAllowed(this.isNestedTransactionAllowed()); -
ConnectionHolder conHolder = (ConnectionHolder)TransactionSynchronizationManager.getResource(this.obtainDataSource()); -
txObject.setConnectionHolder(conHolder, false); -
return txObject; -
}
这里还获取了ConnectionHolder对象,这里newConnectionHolder为false
获取的方法如下:
org.springframework.transaction.support.TransactionSynchronizationManager#getResource
-
@Nullable -
public static Object getResource(Object key) { -
Object actualKey = TransactionSynchronizationUtils.unwrapResourceIfNecessary(key); -
Object value = doGetResource(actualKey); -
if (value != null && logger.isTraceEnabled()) { -
logger.trace("Retrieved value [" + value + "] for key [" + actualKey + "] bound to thread [" + Thread.currentThread().getName() + "]"); -
} -
return value; -
}
看代码很有意思,好像是通过一个key获取的,类似于从一个池子里面拿东西一样,其实当A方法执行的时候并没有获取到ConnectionHolder,拿到的是null
org.springframework.transaction.support.TransactionSynchronizationManager#doGetResource
-
private static Object doGetResource(Object actualKey) { -
Map<Object, Object> map = (Map)resources.get(); -
if (map == null) { -
return null; -
} else { -
Object value = map.get(actualKey); -
if (value instanceof ResourceHolder && ((ResourceHolder)value).isVoid()) { -
map.remove(actualKey); -
if (map.isEmpty()) { -
resources.remove(); -
} -
value = null; -
} -
return value; -
} -
}

resources对象其实是一个ThreadLocal,意思是同一个线程中拿到的ConnectionHolder是相同的
3.3 doBegin方法
org.springframework.jdbc.datasource.DataSourceTransactionManager#doBegin
-
protected void doBegin(Object transaction, TransactionDefinition definition) { -
DataSourceTransactionManager.DataSourceTransactionObject txObject = (DataSourceTransactionManager.DataSourceTransactionObject)transaction; -
Connection con = null; -
try { -
if (!txObject.hasConnectionHolder() || txObject.getConnectionHolder().isSynchronizedWithTransaction()) { -
Connection newCon = this.obtainDataSource().getConnection(); -
if (this.logger.isDebugEnabled()) { -
this.logger.debug("Acquired Connection [" + newCon + "] for JDBC transaction"); -
} -
txObject.setConnectionHolder(new ConnectionHolder(newCon), true); -
} -
txObject.getConnectionHolder().setSynchronizedWithTransaction(true); -
con = txObject.getConnectionHolder().getConnection(); -
Integer previousIsolationLevel = DataSourceUtils.prepareConnectionForTransaction(con, definition); -
txObject.setPreviousIsolationLevel(previousIsolationLevel); -
if (con.getAutoCommit()) { -
txObject.setMustRestoreAutoCommit(true); -
if (this.logger.isDebugEnabled()) { -
this.logger.debug("Switching JDBC Connection [" + con + "] to manual commit"); -
} -
con.setAutoCommit(false); -
} -
this.prepareTransactionalConnection(con, definition); -
txObject.getConnectionHolder().setTransactionActive(true); -
int timeout = this.determineTimeout(definition); -
if (timeout != -1) { -
txObject.getConnectionHolder().setTimeoutInSeconds(timeout); -
} -
if (txObject.isNewConnectionHolder()) { -
TransactionSynchronizationManager.bindResource(this.obtainDataSource(), txObject.getConnectionHolder()); -
} -
} catch (Throwable var7) { -
if (txObject.isNewConnectionHolder()) { -
DataSourceUtils.releaseConnection(con, this.obtainDataSource()); -
txObject.setConnectionHolder((ConnectionHolder)null, false); -
} -
throw new CannotCreateTransactionException("Could not open JDBC Connection for transaction", var7); -
} -
}

截取该方法的重要几行:
-
Connection newCon = this.obtainDataSource().getConnection(); -
if (this.logger.isDebugEnabled()) { -
this.logger.debug("Acquired Connection [" + newCon + "] for JDBC transaction"); -
} -
txObject.setConnectionHolder(new ConnectionHolder(newCon), true);
先获取连接(java.sql.Connection),然后创建ConnectionHolder,newConnectionHolder设置为true,如果之前已经不为空了,newConnectionHolder就为false
如果newConnectionHolder 为 true,还需要将 connectionHolder放到threadLocal里面,让后面的方法可以获取到相同的ConnectionHolder,截取的代码如下:
-
if (txObject.isNewConnectionHolder()) { -
TransactionSynchronizationManager.bindResource(this.obtainDataSource(), txObject.getConnectionHolder()); -
}
到这里TransactionStatus就创建好了
3.3 获取TransactionInfo
org.springframework.transaction.interceptor.TransactionAspectSupport#prepareTransactionInfo
-
protected TransactionAspectSupport.TransactionInfo prepareTransactionInfo(@Nullable PlatformTransactionManager tm, @Nullable TransactionAttribute txAttr, String joinpointIdentification, @Nullable TransactionStatus status) { -
TransactionAspectSupport.TransactionInfo txInfo = new TransactionAspectSupport.TransactionInfo(tm, txAttr, joinpointIdentification); -
if (txAttr != null) { -
if (this.logger.isTraceEnabled()) { -
this.logger.trace("Getting transaction for [" + txInfo.getJoinpointIdentification() + "]"); -
} -
txInfo.newTransactionStatus(status); -
} else if (this.logger.isTraceEnabled()) { -
this.logger.trace("Don't need to create transaction for [" + joinpointIdentification + "]: This method isn't transactional."); -
} -
txInfo.bindToThread(); -
return txInfo; -
}

细看 txInfo.bindToThread();
org.springframework.transaction.interceptor.TransactionAspectSupport.TransactionInfo#bindToThread
-
private void bindToThread() { -
this.oldTransactionInfo = (TransactionAspectSupport.TransactionInfo)TransactionAspectSupport.transactionInfoHolder.get(); -
TransactionAspectSupport.transactionInfoHolder.set(this); -
}
java.lang.ThreadLocal#get
-
public T get() { -
Thread t = Thread.currentThread(); -
ThreadLocalMap map = getMap(t); -
if (map != null) { -
ThreadLocalMap.Entry e = map.getEntry(this); -
if (e != null) { -
@SuppressWarnings("unchecked") -
T result = (T)e.value; -
return result; -
} -
} -
return setInitialValue(); -
}
bindToThread()的的作用是获取oldTransactionInfo,还有线程有关(具体原理未知)
4.B方法抛异常后的回滚操作
org.springframework.transaction.interceptor.TransactionAspectSupport#completeTransactionAfterThrowing
-
protected void completeTransactionAfterThrowing(@Nullable TransactionAspectSupport.TransactionInfo txInfo, Throwable ex) { -
if (txInfo != null && txInfo.getTransactionStatus() != null) { -
if (this.logger.isTraceEnabled()) { -
this.logger.trace("Completing transaction for [" + txInfo.getJoinpointIdentification() + "] after exception: " + ex); -
} -
if (txInfo.transactionAttribute != null && txInfo.transactionAttribute.rollbackOn(ex)) { -
try { -
txInfo.getTransactionManager().rollback(txInfo.getTransactionStatus()); -
} catch (TransactionSystemException var6) { -
this.logger.error("Application exception overridden by rollback exception", ex); -
var6.initApplicationException(ex); -
throw var6; -
} catch (Error | RuntimeException var7) { -
this.logger.error("Application exception overridden by rollback exception", ex); -
throw var7; -
} -
} else { -
try { -
txInfo.getTransactionManager().commit(txInfo.getTransactionStatus()); -
} catch (TransactionSystemException var4) { -
this.logger.error("Application exception overridden by commit exception", ex); -
var4.initApplicationException(ex); -
throw var4; -
} catch (Error | RuntimeException var5) { -
this.logger.error("Application exception overridden by commit exception", ex); -
throw var5; -
} -
} -
} -
}

txInfo.getTransactionManager().rollback(txInfo.getTransactionStatus());调用transactionManager进行rollback
org.springframework.transaction.support.AbstractPlatformTransactionManager#rollback
-
public final void rollback(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"); -
} else { -
DefaultTransactionStatus defStatus = (DefaultTransactionStatus)status; -
this.processRollback(defStatus, false); -
} -
}
进一步调自身的processRollback
org.springframework.transaction.support.AbstractPlatformTransactionManager#processRollback
-
private void processRollback(DefaultTransactionStatus status, boolean unexpected) { -
try { -
boolean unexpectedRollback = unexpected; -
try { -
this.triggerBeforeCompletion(status); -
if (status.hasSavepoint()) { -
if (status.isDebug()) { -
this.logger.debug("Rolling back transaction to savepoint"); -
} -
status.rollbackToHeldSavepoint(); -
} else if (status.isNewTransaction()) { -
if (status.isDebug()) { -
this.logger.debug("Initiating transaction rollback"); -
} -
this.doRollback(status); -
} else { -
if (status.hasTransaction()) { -
if (!status.isLocalRollbackOnly() && !this.isGlobalRollbackOnParticipationFailure()) { -
if (status.isDebug()) { -
this.logger.debug("Participating transaction failed - letting transaction originator decide on rollback"); -
} -
} else { -
if (status.isDebug()) { -
this.logger.debug("Participating transaction failed - marking existing transaction as rollback-only"); -
} -
this.doSetRollbackOnly(status); -
} -
} else { -
this.logger.debug("Should roll back transaction but cannot - no transaction available"); -
} -
if (!this.isFailEarlyOnGlobalRollbackOnly()) { -
unexpectedRollback = false; -
} -
} -
} catch (Error | RuntimeException var8) { -
this.triggerAfterCompletion(status, 2); -
throw var8; -
} -
this.triggerAfterCompletion(status, 1); -
if (unexpectedRollback) { -
throw new UnexpectedRollbackException("Transaction rolled back because it has been marked as rollback-only"); -
} -
} finally { -
this.cleanupAfterCompletion(status); -
} -
}

this.triggerBeforeCompletion(status) 这个方法好像释放了连接
B不是新事务,所以最后会执行 this.doSetRollbackOnly(status);
org.springframework.jdbc.datasource.DataSourceTransactionManager#doSetRollbackOnly
-
protected void doSetRollbackOnly(DefaultTransactionStatus status) { -
DataSourceTransactionManager.DataSourceTransactionObject txObject = (DataSourceTransactionManager.DataSourceTransactionObject)status.getTransaction(); -
if (status.isDebug()) { -
this.logger.debug("Setting JDBC transaction [" + txObject.getConnectionHolder().getConnection() + "] rollback-only"); -
} -
txObject.setRollbackOnly(); -
}
org.springframework.jdbc.datasource.DataSourceTransactionManager.DataSourceTransactionObject#setRollbackOnly
-
public void setRollbackOnly() { -
this.getConnectionHolder().setRollbackOnly(); -
}
这里可以看到最终设置的是connectionHolder的rollbackonly属性
5.A方法尝试提交时的操作
org.springframework.transaction.interceptor.TransactionAspectSupport#commitTransactionAfterReturning
-
protected void commitTransactionAfterReturning(@Nullable TransactionAspectSupport.TransactionInfo txInfo) { -
if (txInfo != null && txInfo.getTransactionStatus() != null) { -
if (this.logger.isTraceEnabled()) { -
this.logger.trace("Completing transaction for [" + txInfo.getJoinpointIdentification() + "]"); -
} -
txInfo.getTransactionManager().commit(txInfo.getTransactionStatus()); -
} -
}
同样的,这里调用transactionManager进行提交
org.springframework.transaction.support.AbstractPlatformTransactionManager#commit
-
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"); -
} else { -
DefaultTransactionStatus defStatus = (DefaultTransactionStatus)status; -
if (defStatus.isLocalRollbackOnly()) { -
if (defStatus.isDebug()) { -
this.logger.debug("Transactional code has requested rollback"); -
} -
this.processRollback(defStatus, false); -
} else if (!this.shouldCommitOnGlobalRollbackOnly() && defStatus.isGlobalRollbackOnly()) { -
if (defStatus.isDebug()) { -
this.logger.debug("Global transaction is marked as rollback-only but transactional code requested commit"); -
} -
this.processRollback(defStatus, true); -
} else { -
this.processCommit(defStatus); -
} -
} -
}

这个方法判断了一些无法提交的情况,程序这里走第二个分支,部分代码如下:
-
else if (!this.shouldCommitOnGlobalRollbackOnly() && defStatus.isGlobalRollbackOnly()) { -
if (defStatus.isDebug()) { -
this.logger.debug("Global transaction is marked as rollback-only but transactional code requested commit"); -
} -
this.processRollback(defStatus, true); -
}
判断条件为:
1.全局不是rollbackonly的时候也提交(这个可能是一个配置的参数,配合在rollbackonly的时候也提交,也就是出现现在这种情况后,不用回滚,直接提交)
2.并且全局是rollbackonly
org.springframework.transaction.support.DefaultTransactionStatus#isGlobalRollbackOnly
-
public boolean isGlobalRollbackOnly() { -
return this.transaction instanceof SmartTransactionObject && ((SmartTransactionObject)this.transaction).isRollbackOnly(); -
}
这里又要满足两个条件
1 .这里的transaction是DataSourceTransactionObject
DataSourceTransaction继承 JdbcTransactionObjectSupport
JdbcTransactionObjectSupport又实现 SmartTransactionObject,所以第一个条件满足
2. DatSourceTransactionObject的 RollbackOnly 的get和set方法如下
-
public void setRollbackOnly() { -
this.getConnectionHolder().setRollbackOnly(); -
} -
public boolean isRollbackOnly() { -
return this.getConnectionHolder().isRollbackOnly(); -
}
之前B方法抛出异常的时候,就是调用的DataSourceTransactionObject的set方法设置rollbackonly为true,现在再用get方法获取,只要是同一个connectionHolder,A获取到的rollbackOnly就是true,就会触发回滚,执行 this.processRollback(defStatus, true);
org.springframework.transaction.support.AbstractPlatformTransactionManager#processRollback
-
private void processRollback(DefaultTransactionStatus status, boolean unexpected) { -
try { -
boolean unexpectedRollback = unexpected; -
try { -
this.triggerBeforeCompletion(status); -
if (status.hasSavepoint()) { -
if (status.isDebug()) { -
this.logger.debug("Rolling back transaction to savepoint"); -
} -
status.rollbackToHeldSavepoint(); -
} else if (status.isNewTransaction()) { -
if (status.isDebug()) { -
this.logger.debug("Initiating transaction rollback"); -
} -
this.doRollback(status); -
} else { -
if (status.hasTransaction()) { -
if (!status.isLocalRollbackOnly() && !this.isGlobalRollbackOnParticipationFailure()) { -
if (status.isDebug()) { -
this.logger.debug("Participating transaction failed - letting transaction originator decide on rollback"); -
} -
} else { -
if (status.isDebug()) { -
this.logger.debug("Participating transaction failed - marking existing transaction as rollback-only"); -
} -
this.doSetRollbackOnly(status); -
} -
} else { -
this.logger.debug("Should roll back transaction but cannot - no transaction available"); -
} -
if (!this.isFailEarlyOnGlobalRollbackOnly()) { -
unexpectedRollback = false; -
} -
} -
} catch (Error | RuntimeException var8) { -
this.triggerAfterCompletion(status, 2); -
throw var8; -
} -
this.triggerAfterCompletion(status, 1); -
if (unexpectedRollback) { -
throw new UnexpectedRollbackException("Transaction rolled back because it has been marked as rollback-only"); -
} -
} finally { -
this.cleanupAfterCompletion(status); -
} -
}

到这里 unexpectedRollback为true,就抛出了 "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, propagation = Propagation.REQUIRES_NEW)
public void methodA(){
System.out.println(1 / 0);
}
@Transactional(rollbackFor = Exception.class)
public void methodB(){
try{
insert();
methodA();
}catch(Exception e){
System.out.println("有异常");
}
}



















