正文
四、提交事务
提交事务的代码要从commitTransactionAfterReturning开始说起。
protected void commitTransactionAfterReturning(@Nullable TransactionInfo txInfo) { //判断当前有事务信息,并且事务状态不为空,才允许提交事务 if (txInfo != null && txInfo.getTransactionStatus() != null) { if (logger.isTraceEnabled()) { logger.trace("Completing transaction for [" + txInfo.getJoinpointIdentification() + "]"); } //提交事务 txInfo.getTransactionManager().commit(txInfo.getTransactionStatus()); } }
@Override 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, false); return; } //如果没有配置全局回滚,并且当前状态需要回滚 if (!shouldCommitOnGlobalRollbackOnly() && defStatus.isGlobalRollbackOnly()) { if (defStatus.isDebug()) { logger.debug("Global transaction is marked as rollback-only but transactional code requested commit"); } //回滚事务 processRollback(defStatus, true); return; } //提交事务 processCommit(defStatus); }
执行事务提交之前,先判断一下事务状态是否需要提交,如果有需要回滚的则回滚,最后提交事务交给processCommit方法。
private void processCommit(DefaultTransactionStatus status) throws TransactionException { try { boolean beforeCompletionInvoked = false; try { boolean unexpectedRollback = false; //空实现 prepareForCommit(status); //提交会话 triggerBeforeCommit(status); //解绑线程和会话绑定关系,关闭会话 triggerBeforeCompletion(status); beforeCompletionInvoked = true; //如果配置了回滚点,说明是嵌套事务 if (status.hasSavepoint()) { if (status.isDebug()) { logger.debug("Releasing transaction savepoint"); } unexpectedRollback = status.isGlobalRollbackOnly(); //清除回滚点 status.releaseHeldSavepoint(); } //如果是新事务 else if (status.isNewTransaction()) { if (status.isDebug()) { logger.debug("Initiating transaction commit"); } unexpectedRollback = status.isGlobalRollbackOnly(); //提交事务 doCommit(status); } else if (isFailEarlyOnGlobalRollbackOnly()) { unexpectedRollback = status.isGlobalRollbackOnly(); } if (unexpectedRollback) { throw new UnexpectedRollbackException( "Transaction silently rolled back because it has been marked as rollback-only"); } } catch (UnexpectedRollbackException ex) { //关闭会话工厂,关闭会话,重置属性 triggerAfterCompletion(status, TransactionSynchronization.STATUS_ROLLED_BACK); throw ex; } catch (TransactionException ex) { // can only be caused by doCommit if (isRollbackOnCommitFailure()) { doRollbackOnCommitException(status, ex); } else { //关闭会话工厂,关闭会话,重置属性 triggerAfterCompletion(status, TransactionSynchronization.STATUS_UNKNOWN); } throw ex; } catch (RuntimeException | Error ex) { if (!beforeCompletionInvoked) { triggerBeforeCompletion(status); } doRollbackOnCommitException(status, ex); throw ex; } try { //空实现 triggerAfterCommit(status); } finally { //关闭会话工厂,关闭会话,重置属性 triggerAfterCompletion(status, TransactionSynchronization.STATUS_COMMITTED); } } finally { //解绑数据库连接,重置并关闭数据库连接 //恢复挂起的资源 cleanupAfterCompletion(status); } }
我再用一张流程图梳理一下整个流程
doCommit方法代码很简单
@Override protected void doCommit(DefaultTransactionStatus status) { DataSourceTransactionObject txObject = (DataSourceTransactionObject) status.getTransaction(); Connection con = txObject.getConnectionHolder().getConnection(); if (status.isDebug()) { logger.debug("Committing JDBC transaction on Connection [" + con + "]"); } try { con.commit(); } catch (SQLException ex) { throw new TransactionSystemException("Could not commit JDBC transaction", ex); } }
就是调用数据库连接的commit方法,清空回滚点也是调用数据库连接的releaseSavepoint方法,比较简单在这里就不深入介绍了。
五、回滚事务
回滚事务入口在completeTransactionAfterThrowing方法
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); } //如果属性属性不为空,并且需要回滚 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 { 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; } } } }
我们重点看一下是如何判断要回滚事务的,看一下txInfo.transactionAttribute.rollbackOn方法
@Override public boolean rollbackOn(Throwable ex) { return (ex instanceof RuntimeException || ex instanceof Error); }
这里又引出一个问题:
默认情况下,事务是否需要回滚是根据异常类型判断的,如果异常类型为RuntimeException或者Error,则需要回滚。那么问题来了,如果异常类型是Exception需要回滚不?如果不需要回滚,怎样才能让异常类型是Exception的事务回滚?
重点看一下事务回滚方法rollback
@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方法
private void processRollback(DefaultTransactionStatus status, boolean unexpected) { try { boolean unexpectedRollback = unexpected; try { //解绑线程和会话绑定关系,关闭会话 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 { // jta事务 if (status.hasTransaction()) { //如果是本地回滚或者全局回滚 if (status.isLocalRollbackOnly() || isGlobalRollbackOnParticipationFailure()) { 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"); } // Unexpected rollback only matters here if we're asked to fail early if (!isFailEarlyOnGlobalRollbackOnly()) { unexpectedRollback = false; } } } catch (RuntimeException | Error ex) { //关闭会话工厂,关闭会话,重置属性 triggerAfterCompletion(status, TransactionSynchronization.STATUS_UNKNOWN); throw ex; } //关闭会话工厂,关闭会话,重置属性 triggerAfterCompletion(status, TransactionSynchronization.STATUS_ROLLED_BACK); // Raise UnexpectedRollbackException if we had a global rollback-only marker if (unexpectedRollback) { throw new UnexpectedRollbackException( "Transaction rolled back because it has been marked as rollback-only"); } } finally { //解绑数据库连接,重置并关闭数据库连接 //恢复挂起的资源 cleanupAfterCompletion(status); } }
再用一张流程图梳理一下流程
事务还有很多有意思的代码,由于篇幅的原因暂时先介绍到这里。