rollbackOnly通常发生在嵌套事务中
事务传播机制为默认Propagation.REQUIRED
A > X > X > X > B
A和B方法都有@Transactional注解,A中调用B,呈嵌套事务,公用一个事务(最外层A方法的事务)
当B发生异常时,Transactional会将当前rollbackOnly属性标记为true,因为异常被捕获(只要异常没有传到最外层的A),在处理结束A需要提交事务的时候,会执行JdbcResourceLocalTransactionCoordinatorImpl.commit()方法提交事务
@Override
public void commit() {
try {
if ( rollbackOnly ) {
log.debugf( "On commit, transaction was marked for roll-back only, rolling back" );
try {
rollback();
if ( jpaCompliance.isJpaTransactionComplianceEnabled() ) {
log.debugf( "Throwing RollbackException on roll-back of transaction marked rollback-only on commit" );
throw new RollbackException( "Transaction was marked for rollback-only" );
}
return;
}
catch (RollbackException e) {
throw e;
}
catch (RuntimeException e) {
log.debug( "Encountered failure rolling back failed commit", e );
throw e;
}
}
JdbcResourceLocalTransactionCoordinatorImpl.this.beforeCompletionCallback();
jdbcResourceTransaction.commit();
JdbcResourceLocalTransactionCoordinatorImpl.this.afterCompletionCallback( true );
}
catch (RollbackException e) {
throw e;
}
catch (RuntimeException e) {
try {
rollback();
}
catch (RuntimeException e2) {
log.debug( "Encountered failure rolling back failed commit", e2 );
}
throw e;
}
}
commit()会检查当前是否被标记为rollbackOnly,否则抛出
throw new RollbackException( "Transaction was marked for rollback-only" );
解决办法:
1:保证同一个事务链的调用链没有出现捕获异常不抛出的情况
2:使用REQUIRES_NEW的隔离级别
我是因为采用了责任链模式,有点抽象,有一个不起眼环节异常被捕获而导致的
Propagation.REQUIRES_NEW
作用有两个
1:新开一个事务,外层事务不会影响内层事务的提交和回滚
2:如果内层事务出现异常,外层事务也会进行回滚