异常堆栈:
org.springframework.transaction.UnexpectedRollbackException: Transaction rolled back because it has been marked as rollback-only
日常工作中遇到如上错误,总结如下:
发生异常的场景描述:在使用Spring事务时,在一个事务A中又开了一个事务B(即存在嵌套事务),当事务B发生异常时,将异常catch后事务B会进行回滚操作,若此时异常被直接吃掉(即事务A无法得知发生过异常),则事务A会抛出如上异常。
根本原因:spring事务发生嵌套时,默认的传播机制是“PROPAGATION_REQUIRED” ,“PROPAGATION_REQUIRED”表示如果当前没有事务,则新建一个事务;若当前存在事务则加入到此事务中。当使用此传播机制时,针对上述场景,事务A和事务B其实是同一个事务,当事务B回滚时已经将事务标记为已回滚,此时当事务A再次执行conmint时就会报上述异常。
Spring事务conmint源码如下:
/**
* This implementation of commit handles participating in existing
* transactions and programmatic rollback requests.
* Delegates to <code>isRollbackOnly</code>, <code>doCommit</code>
* and <code>rollback</code>.
* @see org.springframework.transaction.TransactionStatus#isRollbackOnly()
* @see #doCommit
* @see #rollback
*/
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);
return;
}
if (!shouldCommitOnGlobalRollbackOnly() && defStatus.isGlobalRollbackOnly()) {
if (defStatus.isDebug()) {
logger.debug("Global transaction is marked as rollback-only but transactional code requested commit");
}
processRollback(defStatus);
// Throw UnexpectedRollbackException only at outermost transaction boundary
// or if explicitly asked to.
if (status.isNewTransaction() || isFailEarlyOnGlobalRollbackOnly()) {
throw new UnexpectedRollbackException(
"Transaction rolled back because it has been marked as rollback-only");
}
return;
}
processCommit(defStatus);
}
Spring事务传播机制汇总如下:
- PROPAGATION_REQUIRED:如果当前没有事务,就新建一个事务,如果已经存在一个事务,就加入到这个事务中。默认策略
- PROPAGATION_SUPPORTS:支持当前事务,如果当前没有事务,就以非事务方式执行。
- PROPAGATION_MANDATORY:使用当前的事务,如果当前没有事务,就抛出异常。
- PROPAGATION_REQUIRES_NEW:新建事务,如果当前存在事务,把当前事务挂起。
- PROPAGATION_NOT_SUPPORTED:以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。
- PROPAGATION_NEVER:以非事务方式执行,如果当前存在事务,则抛出异常。
- PROPAGATION_NESTED:如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则执行与PROPAGATION_REQUIRED类似的操作。