这篇文章讲事务剩下的回滚和提交。
事务的回滚处理
之前已经完成了目标方法运行前的事务准备工作。而这些准备工作的最大目的无非就是对于程序没有按照我们期待的那样进行,也就是出现特定的错误;那么当出现错误的时候Spring是怎么对数据进行恢复的呢?我们先来看一下 TransactionAspectSupport
类里的 invokeWithinTransaction
函数的 completeTransactionAfterThrowing
方法(其它的已经在上一篇文章讲解过,这里不再赘述):
- 看源码(
TransactionAspectSupport.java
)
protected void completeTransactionAfterThrowing(@Nullable TransactionInfo txInfo, Throwable ex) { // 当抛出异常时,先判断当前是否存在事务,这是基础依据 if (txInfo != null && txInfo.getTransactionStatus() != null) { if (logger.isTraceEnabled()) { logger.trace("Completing transaction for [" + txInfo.getJoinpointIdentification() + "] after exception: " + ex); } // 这里判断是否回滚默认的 依据是抛出的异常是RunTimeException 或者是 Erro 类型r if (txInfo.transactionAttribute != null && txInfo.transactionAttribute.rollbackOn(ex)) { try { txInfo.getTransactionManager().rollback(txInfo.getTransactionStatus()); } catch (TransactionSystemException ex2) { logger.error("Application exception overridden by rollback exception", ex); ex2.initApplicationException(ex); throw ex2; } catch (RuntimeException | Error ex2) { logger.error("Application exception overridden by rollback exception", ex); throw ex2; } } else { // We don't roll back on this exception. // Will still roll back if TransactionStatus.isRollbackOnly() is true. // 如果不满足回滚条件,即使抛出异常也正常提交 try { txInfo.getTransactionManager().commit(txInfo.getTransactionStatus()); } catch (TransactionSystemException ex2) { logger.error("Application exception overridden by commit exception", ex); ex2.initApplicationException(ex); throw ex2; } catch (RuntimeException | Error ex2) { logger.error("Application exception overridden by commit exception", ex); throw ex2; } } } }
-
源码分析
在对目标方法的执行过程中,一旦出现 Throwable
就会被引导至此方法进行处理,但是不意味着所有的 Throwable
都会被回滚处理;比如我么最常用的Exception,默认是不会被处理的,默认情况下,即使出现异常,数据也会被正常提交,而这个关键的地方就在于 txInfo.transactionAttribute.rollbackOn(ex)
这个函数:
回滚条件
- 看源码(
DefaultTransactionAttribute.java
)
@Override public Boolean rollbackOn(Throwable ex) { return (ex instanceof RuntimeException || ex instanceof Error); }
-
源码分析
从上述代码中可以看到,默认情况下:Spring只会对 RuntimeException
和 Error
两种类型的情况进行处理;但是我们可以利用注解方式来改变。例如:
@Transactional(rollbackFor = Exception.class, propagation = Propagation.REQUIRED)
回滚处理
当然,一旦符合回滚条件,那么Spring就会将程序引导至回滚处理函数中。接下来我们看一下回滚函数,也就是 txInfo.getTransactionManager().rollback(txInfo.getTransactionStatus());
代码的rollback方法:
- 看源码(
AbstractPlatformTransactionManager.java
)
@Override 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"); } DefaultTransactionStatus defStatus = (DefaultTransactionStatus) status; processRollback(defStatus, false); }
看一下这个方法里面的 processRollback
函数:
- 看源码(
AbstractPlatformTransactionManager.java
)
private void processRollback(DefaultTransactionStatus status, Boolean unexpected) { try { Boolean unexpectedRollback = unexpected; try { triggerBeforeCompletion(status); // 如果status 有 savePoint , 说明此事务 是 PROPAGATION_NESTED 且为子事务,只能回滚到savePoint if (status.hasSavepoint()) { if (status.isDebug()) { logger.debug("Rolling back transaction to savepoint"); } // 回滚到保存点 status.rollbackToHeldSavepoint(); } // 如果此时的status显示的是新的事务,才进行回滚 else if (status.isNewTransaction()) { if (status.isDebug()) { logger.debug("Initiating transaction rollback"); }