返回介绍

10.3.2 回滚处理

发布于 2025-04-22 22:09:16 字数 10578 浏览 0 评论 0 收藏

之前已经完成了目标方法运行前的事务准备工作,而这些准备工作最大的目的无非是对于程序没有按照我们期待的那样进行,也就是出现特定的错误,那么,当出现错误的时候,Spring 是怎么对数据进行恢复的呢?

protected void completeTransactionAfterThrowing(TransactionInfo txInfo, Throwable ex) {

 //当抛出异常时首先判断当前是否存在事务,这是基础依据

  if (txInfo != null && txInfo.hasTransaction()) {

   if (logger.isTraceEnabled()) {

    Identification() +

    logger.trace("Completing transaction for [" + txInfo.getJoinpoint

    "] after exception: " + ex);

  }

  //这里判断是否回滚默认的依据是抛出的异常是否是 RuntimeException 或者是 Error 的类型

   if (txInfo.transactionAttribute.rollbackOn(ex)) {

    try {

    //根据 TransactionStatus 信息进行回滚处理

    txInfo.getTransactionManager().rollback(txInfo.GetTransaction

    Status());

   }

    catch (TransactionSystemException ex2) {

     logger.error("Application exception overridden by rollback

     exception", ex);

    ex2.initApplicationException(ex);

     throw ex2;

   }

    catch (RuntimeException ex2) {

     logger.error("Application exception overridden by rollback

     exception", ex);

     throw ex2;

   }

    catch (Error err) {

     logger.error("Application exception overridden by rollback error", ex);

     throw err;

   }

   }else {

   //如果不满足回滚条件即使抛出异常也同样会提交

    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 ex2) {

     logger.error("Application exception overridden by commit

     exception", ex);

     throw ex2;

   }

    catch (Error err) {

     logger.error("Application exception overridden by commit error", ex);

     throw err;

   }

  }

 }

}

在对目标方法的执行过程中,一旦出现 Throwable 就会被引导至此方法处理,但是并不代表所有的 Throwable 都会被回滚处理,比如我们最常用的 Exception,默认是不会被处理的。默认情况下,即使出现异常,数据也会被正常提交,而这个关键的地方就是在 txInfo.transaction Attribute.rollbackOn(ex) 这个函数。

1.回滚条件

public boolean rollbackOn(Throwable ex) {

  return (ex instanceof RuntimeException || ex instanceof Error);

}

看到了吗?默认情况下 Spring 中的事务异常处理机制只对 RuntimeException 和 Error 两种情况感兴趣,当然你可以通过扩展来改变,不过,我们最常用的还是使用事务提供的属性设置,利用注解方式的使用,例如:

@Transactional(propagation=Propagation.REQUIRED,rollbackFor=Exception.class)

2.回滚处理

当然,一旦符合回滚条件,那么 Spring 就会将程序引导至回滚处理函数中。

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);

}

private void processRollback(DefaultTransactionStatus status) {

  try {

   try {

   //激活所有 TransactionSynchronization 中对应的方法

   triggerBeforeCompletion(status);

    if (status.hasSavepoint()) {

     if (status.isDebug()) {

      logger.debug("Rolling back transaction to savepoint");

    }

    //如果有保存点,也就是当前事务为单独的线程则会退到保存点

    status.rollbackToHeldSavepoint();

   }

    else if (status.isNewTransaction()) {

     if (status.isDebug()) {

      logger.debug("Initiating transaction rollback");

    }

    //如果当前事务为独立的新事物,则直接回退

    doRollback(status);

   }

    else if (status.hasTransaction()) {

     if (status.isLocalRollbackOnly() || isGlobalRollbackOnParticipation

     Failure()) {

     if (status.isDebug()) {

      logger.debug("Participating transaction failed -

      marking existing transaction as rollback-only");

    }

    //如果当前事务不是独立的事务,那么只能标记状态,等到事务链执行完毕后统

    一回滚

    doSetRollbackOnly(status);

   }

    else {

     if (status.isDebug()) {

      logger.debug("Participating transaction failed -

      letting transaction originator decide on rollback");

    }

   }

  }

   else {

    logger.debug("Should roll back transaction but cannot - no

    transaction available");

  }

 }

  catch (RuntimeException ex) {

   triggerAfterCompletion(status, TransactionSynchronization.STATUS_UNKNOWN);

   throw ex;

 }

  catch (Error err) {

   triggerAfterCompletion(status, TransactionSynchronization.STATUS_UNKNOWN);

   throw err;

 }

 //激活所有 TransactionSynchronization 中对应的方法

  triggerAfterCompletion(status, TransactionSynchronization.STATUS_ROLLED_BACK);

 }

  finally {

 //清空记录的资源并将挂起的资源恢复

 cleanupAfterCompletion(status);

 }

}

同样,对于在 Spring 中的复杂的逻辑处理过程,在入口函数一般都会给出个整体的处理脉络,而把实现细节委托给其他函数去执行。我们尝试总结下 Spring 中对于回滚处理的大致脉络如下。

(1)首先是自定义触发器的调用,包括在回滚前、完成回滚后的调用,当然完成回滚包括正常回滚与回滚过程中出现异常,自定义的触发器会根据这些信息作进一步处理,而对于触发器的注册,常见是在回调过程中通过 TransactionSynchronizationManager 类中的静态方法直接注册:

public static void registerSynchronization(TransactionSynchronization synchronization)

(2)除了触发监听函数外,就是真正的回滚逻辑处理了。

当之前已经保存的事务信息中有保存点信息的时候,使用保存点信息进行回滚。常用于嵌入式事务,对于嵌入式的事务的处理,内嵌的事务异常并不会引起外部事务的回滚。

根据保存点回滚的实现方式其实是根据底层的数据库连接进行的。

public void rollbackToHeldSavepoint() throws TransactionException {

  if (!hasSavepoint()) {

   throw new TransactionUsageException("No savepoint associated with current

  transaction");

 }

 getSavepointManager().rollbackToSavepoint(getSavepoint());

 setSavepoint(null);

}

这里使用的是 JDBC 的方式进行数据库连接,那么 getSavepointManager() 函数返回的是 JdbcTransactionObjectSupport ,也就是说上面函数会调用 JdbcTransactionObjectSupport 中的 rollbackToSavepoint 方法。

public void rollbackToSavepoint(Object savepoint) throws TransactionException {

  try {

  getConnectionHolderForSavepoint().getConnection().rollback((Savepoint)

  savepoint);

 }

  catch (Throwable ex) {

   throw new TransactionSystemException("Could not roll back to JDBC savepoint", ex);

 }

}

当之前已经保存的事务信息中的事务为新事物,那么直接回滚。常用于单独事务的处理。对于没有保存点的回滚,Spring 同样是使用底层数据库连接提供的 API 来操作的。由于我们使用的是 DataSourceTransactionManager,那么 doRollback 函数会使用此类中的实现:

protected void doRollback(DefaultTransactionStatus status) {

  DataSourceTransactionObject txObject = (DataSourceTransactionObject)

 status.getTransaction();

  Connection con = txObject.getConnectionHolder().getConnection();

  if (status.isDebug()) {

   logger.debug("Rolling back JDBC transaction on Connection [" + con + "]");

 }

  try {

  con.rollback();

 }

  catch (SQLException ex) {

   throw new TransactionSystemException("Could not roll back JDBC transaction", ex);

 }

}

当前事务信息中表明是存在事务的,又不属于以上两种情况,多数用于 JTA,只做回滚标识,等到提交的时候统一不提交。

3.回滚后的信息清除

对于回滚逻辑执行结束后,无论回滚是否成功,都必须要做的事情就是事务结束后的收尾工作。

private void cleanupAfterCompletion(DefaultTransactionStatus status) {

 //设置完成状态

 status.setCompleted();

  if (status.isNewSynchronization()) {

  TransactionSynchronizationManager.clear();

 }

  if (status.isNewTransaction()) {

  doCleanupAfterCompletion(status.getTransaction());

 }

  if (status.getSuspendedResources() != null) {

   if (status.isDebug()) {

    logger.debug("Resuming suspended transaction after completion of inner

   transaction");

  }

  //结束之前事务的挂起状态

  resume(status.getTransaction(), (SuspendedResourcesHolder) status.getSuspended

  Resources());

 }

}

从函数中得知,事务处理的收尾处理工作包括如下内容。

(1)设置状态是对事务信息作完成标识以避免重复调用。

(2)如果当前事务是新的同步状态,需要将绑定到当前线程的事务信息清除。

(3)如果是新事物需要做些清除资源的工作。

protected void doCleanupAfterCompletion(Object transaction) {

  DataSourceTransactionObject txObject = (DataSourceTransactionObject) transaction;

  if (txObject.isNewConnectionHolder()) {

  //将数据库连接从当前线程中解除绑定

  TransactionSynchronizationManager.unbindResource(this.dataSource);

 }

 //释放链接

  Connection con = txObject.getConnectionHolder().getConnection();

  try {

   if (txObject.isMustRestoreAutoCommit()) {

   //恢复数据库连接的自动提交属性

   con.setAutoCommit(true);

  }

  //重置数据库连接

   DataSourceUtils.resetConnectionAfterTransaction(con, txObject.getPrevious

  IsolationLevel());

 }

  catch (Throwable ex) {

   logger.debug("Could not reset JDBC Connection after transaction", ex);

 }

  if (txObject.isNewConnectionHolder()) {

   if (logger.isDebugEnabled()) {

    logger.debug("Releasing JDBC Connection [" + con + "] after

   transaction");

  }

  //如果当前事务时独立的新创建的事务则在事务完成时释放数据库连接

   DataSourceUtils.releaseConnection(con, this.dataSource);

 }

 txObject.getConnectionHolder().clear();

}

(4)如果在事务执行前有事务挂起,那么当前事务执行结束后需要将挂起事务恢复。

protected final void resume(Object transaction, SuspendedResourcesHolder resourcesHolder)

throws TransactionException {

  if (resourcesHolder != null) {

   Object suspendedResources = resourcesHolder.suspendedResources;

   if (suspendedResources != null) {

    doResume(transaction, suspendedResources);

  }

   List<TransactionSynchronization> suspendedSynchronizations = resourcesHolder.

  suspendedSynchronizations;

   if (suspendedSynchronizations != null) {

    TransactionSynchronizationManager.setActualTransactionActive (resources

   Holder.wasActive);

   TransactionSynchronizationManager.setCurrentTransactionIsolationLevel

   (resourcesHolder.isolationLevel);

   TransactionSynchronizationManager.setCurrentTransactionReadOnly

   (resourcesHolder.readOnly);

   TransactionSynchronizationManager.setCurrentTransactionName

   (resourcesHolder.name);

   doResumeSynchronization(suspendedSynchronizations);

  }

 }

}

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据
    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。