上一篇文章讲解了获取事务,并且通过获取的connection设置只读、隔离级别等,这篇文章讲解剩下的事务的回滚和提交
回滚处理
之前已经完成了目标方法运行前的事务准备工作,而这些准备工作最大的目的无非是对于程序没有按照我们期待的那样进行,也就是出现特定的错误,那么,当出现错误的时候,Spring是怎么对数据进行恢复的呢?
1 protected void completeTransactionAfterThrowing(@Nullable TransactionInfo txInfo, Throwable ex) { 2 // 当抛出异常时首先判断当前是否存在事务,这是基础依据 3 if (txInfo != null && txInfo.getTransactionStatus() != null) { 4 if (logger.isTraceEnabled()) { 5 logger.trace("Completing transaction for [" + txInfo.getJoinpointIdentification() + 6 "] after exception: " + ex); 7 } 8 // 这里判断是否回滚默认的依据是抛出的异常是否是RuntimeException或者是Error的类型 9 if (txInfo.transactionAttribute != null && txInfo.transactionAttribute.rollbackOn(ex)) { 10 try { 11 txInfo.getTransactionManager().rollback(txInfo.getTransactionStatus()); 12 } 13 catch (TransactionSystemException ex2) { 14 logger.error("Application exception overridden by rollback exception", ex); 15 ex2.initApplicationException(ex); 16 throw ex2; 17 } 18 catch (RuntimeException | Error ex2) { 19 logger.error("Application exception overridden by rollback exception", ex); 20 throw ex2; 21 } 22 } 23 else { 24 // We don't roll back on this exception. 25 // Will still roll back if TransactionStatus.isRollbackOnly() is true. 26 // 如果不满足回滚条件即使抛出异常也同样会提交 27 try { 28 txInfo.getTransactionManager().commit(txInfo.getTransactionStatus()); 29 } 30 catch (TransactionSystemException ex2) { 31 logger.error("Application exception overridden by commit exception", ex); 32 ex2.initApplicationException(ex); 33 throw ex2; 34 } 35 catch (RuntimeException | Error ex2) { 36 logger.error("Application exception overridden by commit exception", ex); 37 throw ex2; 38 } 39 } 40 } 41 }
在对目标方法的执行过程中,一旦出现Throwable就会被引导至此方法处理,但是并不代表所有的Throwable都会被回滚处理,比如我们最常用的Exception,默认是不会被处理的。 默认情况下,即使出现异常,数据也会被正常提交,而这个关键的地方就是在txlnfo.transactionAttribute.rollbackOn(ex)这个函数。
回滚条件
@Override public boolean rollbackOn(Throwable ex) { return (ex instanceof RuntimeException || ex instanceof Error); }
默认情况下Spring中的亊务异常处理机制只对RuntimeException和Error两种情况感兴趣,我们可以利用注解方式来改变,例如:
@Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
回滚处理
当然,一旦符合回滚条件,那么Spring就会将程序引导至回滚处理函数中。
1 private void processRollback(DefaultTransactionStatus status, boolean unexpected) { 2 try { 3 boolean unexpectedRollback = unexpected; 4 5 try { 6 triggerBeforeCompletion(status); 7 // 如果status有savePoint,说明此事务是NESTD,且为子事务,只回滚到savePoint 8 if (status.hasSavepoint()) { 9 if (status.isDebug()) { 10 logger.debug("Rolling back transaction to savepoint"); 11 } 12 //回滚到保存点 13 status.rollbackToHeldSavepoint(); 14 } 15 // 如果此时的status显示是新的事务才进行回滚 16 else if (status.isNewTransaction()) { 17 if (status.isDebug()) { 18 logger.debug("Initiating transaction rollback"); 19 } 20 //如果此时是子事务,我们想想哪些类型的事务会进入到这里? 21 //回顾上一篇文章中已存在事务的处理,NOT_SUPPORTED创建的Status是prepareTransactionStatus(definition, null, false...),说明是旧事物,并且事务为null,不会进入 22 //REQUIRES_NEW会创建一个新的子事务,Status是newTransactionStatus(definition, transaction, true...)说明是新事务,将会进入到这个分支 23 //PROPAGATION_NESTED创建的Status是prepareTransactionStatus(definition, transaction, false...)是旧事物,使用的是外层的事务,不会进入 24 //PROPAGATION_SUPPORTS 或 PROPAGATION_REQUIRED或PROPAGATION_MANDATORY存在事务加入事务即可,标记为旧事务,prepareTransactionStatus(definition, transaction, false..) 25 //说明当子事务,只有REQUIRES_NEW会进入到这里进行回滚 26 doRollback(status); 27 } 28 else { 29 // Participating in larger transaction 30 // 如果status中有事务,进入下面 31 // 根据上面分析,PROPAGATION_SUPPORTS 或 PROPAGATION_REQUIRED或PROPAGATION_MANDATORY创建的Status是prepareTransactionStatus(definition, transaction, false..) 32 // 如果此事务时子事务,表示存在事务,并且事务为旧事物,将进入到这里 33 if (status.hasTransaction()) { 34 if (status.isLocalRollbackOnly() || isGlobalRollbackOnParticipationFailure()) { 35 if (status.isDebug()) { 36 logger.debug("Participating transaction failed - marking existing transaction as rollback-only"); 37 } 38 // 对status中的transaction作一个回滚了的标记,并不会立即回滚 39 doSetRollbackOnly(status); 40 } 41 else { 42 if (status.isDebug()) { 43 logger.debug("Participating transaction failed - letting transaction originator decide on rollback"); 44 } 45 } 46 } 47 else { 48 logger.debug("Should roll back transaction but cannot - no transaction available"); 49 } 50 // Unexpected rollback only matters here if we're asked to fail early 51 if (!isFailEarlyOnGlobalRollbackOnly()) { 52 unexpectedRollback = false; 53 } 54 } 55 } 56 catch (RuntimeException | Error ex) { 57 triggerAfterCompletion(status, TransactionSynchronization.STATUS_UNKNOWN); 58 throw ex;