Spring事务源码:事务的提交与回滚

参考资料:

《Spring事务源码解析之事务提交篇》

《Spring事务源码解析之事务回滚篇》

《spring事务(四):事务的隔离级别与传播方式的处理02》

《spring事务(五):事务的隔离级别与传播方式的处理03》

前文:

《Spring事务源码:创建代理类》

《Spring事务源码:事务创建》

        写在开头:本文为个人学习笔记,内容比较随意,夹杂个人理解,如有错误,欢迎指正。

前言

        在前文中,我们介绍了事务的创建,事务创建好后就可以执行被代理方法了,执行完后我们还需要根据其执行结果判断是提交还是回滚,本文我们就来深入讲解下这两个操作。

final TransactionAttribute txAttr = getTransactionAttributeSource().getTransactionAttribute(method, targetClass);
final PlatformTransactionManager tm = determineTransactionManager(txAttr);
final String joinpointIdentification = methodIdentification(method, targetClass, txAttr);
// 声明式事务处理
if (txAttr == null || !(tm instanceof CallbackPreferringPlatformTransactionManager)) {
	// 创建事务,事务属性等信息会被保存进TransactionInfo中
	TransactionInfo txInfo = createTransactionIfNecessary(tm, txAttr, joinpointIdentification);
	Object retVal;
	try {
            // 执行目标方法
		    retVal = invocation.proceedWithInvocation();
		}
		catch (Throwable ex) {
		    // 异常处理
			completeTransactionAfterThrowing(txInfo, ex);
			throw ex;
		}
		finally {
            // 清空事务信息
			cleanupTransactionInfo(txInfo);
		}
        // 提交事务
		commitTransactionAfterReturning(txInfo);
	    return retVal;
}

目录

前言

一、事务回滚

        1、completeTransactionAfterThrowing

        2、rollback

        3、processRollback

        3.1、doRollback

        3.2、rollbackToHeldSavepoint

        3.3、doSetRollbackOnly

二、事务提交

        1、commitTransactionAfterReturning

        2、commit

        3、processCommit

        4、doCommit

三、清除事务信息

        1、cleanupAfterCompletion

        1.1、doCleanupAfterCompletion

        1.2、resume

        2、cleanupTransactionInfo


一、事务回滚

        首先来看回滚,在invocation.proceedWithInvocation()异常后将会进入completeTransactionAfterThrowing方法,回滚正是由该方法完成的。

        1、completeTransactionAfterThrowing

        首先判断事务与事务属性是否存在,如果不存在自然也不需要回滚。如果存在则继续判断是否满足回滚的条件满足的话则调用rollback方法进行回滚。

protected void completeTransactionAfterThrowing(TransactionInfo txInfo, Throwable ex) {
    // 判断当前是否存在事务
    if (txInfo != null && txInfo.hasTransaction()) {
        if (logger.isTraceEnabled()) {
            logger.trace("Completing transaction for [" + txInfo.getJoinpointIdentification() +
                "] after exception: " + ex);
        }
        // 判断是否满足回滚条件
        if (txInfo.transactionAttribute.rollbackOn(ex)) {
            try {
                // 回滚处理
                txInfo.getTransactionManager().rollback(txInfo.getTransactionStatus());
            }
            // 其余代码
        }
        else {
            try {
                // 如果不满足回滚条件出现异常也会继续提交
                txInfo.getTransactionManager().commit(txInfo.getTransactionStatus());
            }
            // 其余代码
        }
    }
}

         rollbackOn通过判断异常是否为RuntimeException或Error来决定是否可以回滚,这个条件我们也可以自行配置。

public boolean rollbackOn(Throwable ex) {
    return (ex instanceof RuntimeException || ex instanceof Error);
}

        2、rollback

        首先判断当前事务是否已经完成,如果已经完成则属于异常,否则继续执行processRollback开始回滚。

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

        3、processRollback

        processRollback方法判断有无保存点,是全部回滚还是只回滚到保存点,亦或者需要等到事务链执行完成之后再做回滚。

private void processRollback(DefaultTransactionStatus status) {
    try {
        try {
            // //解绑线程和会话绑定关系
            triggerBeforeCompletion(status);
            if (status.hasSavepoint()) {
                // 如果有保存点,也就是当前事务为单独的线程则会退到保存点
                status.rollbackToHeldSavepoint();
            }
            else if (status.isNewTransaction()) {
                // 如果是新事务直接回滚
                doRollback(status);
            }
            else if (status.hasTransaction()) {
                if (status.isLocalRollbackOnly() || isGlobalRollbackOnParticipationFailure()) {
                    // 如果当前事务不是独立的事务,则只能等待事务链执行完成之后再做回滚操作
                    doSetRollbackOnly(status);
                }
                // 其余代码
        // 关闭会话工厂,关闭会话,重置属性
        triggerAfterCompletion(status, TransactionSynchronization.STATUS_ROLLED_BACK);
    }
    finally {
        // 清理并恢复挂起的事务
        cleanupAfterCompletion(status);
    }
}

        3.1、doRollback

        回滚操作很简介,获取当前线程的数据库连接并调用其rollback方法进行回滚,使用的是底层数据库连接提供的API。

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

        3.2、rollbackToHeldSavepoint

        和上文的doRollback类似,只不过这里多了一个保存点的限制,事务不是全部回滚的而是回滚到保存点即可。

public void rollbackToHeldSavepoint() throws TransactionException {
    if (!hasSavepoint()) {
        throw new TransactionUsageException(
            "Cannot roll back to savepoint - no savepoint associated with current transaction");
    }
    // 回退到保存点
    getSavepointManager().rollbackToSavepoint(getSavepoint());
    getSavepointManager().releaseSavepoint(getSavepoint());
    setSavepoint(null);
}

public void rollbackToSavepoint(Object savepoint) throws TransactionException {
    ConnectionHolder conHolder = getConnectionHolderForSavepoint();
    try {
        // 获取数据库连接后执行回滚,但只回滚到保存点
        conHolder.getConnection().rollback((Savepoint) savepoint);
    }
    catch (Throwable ex) {
        throw new TransactionSystemException("Could not roll back to JDBC savepoint", ex);
    }
}

        3.3、doSetRollbackOnly

        doSetRollbackOnly作用在于人工的设置了回滚标志,不会继续向上抛出异常但是在事务链执行完后进行回滚。

	protected void doSetRollbackOnly(DefaultTransactionStatus status) {
		DataSourceTransactionObject txObject = (DataSourceTransactionObject) status.getTransaction();
		if (status.isDebug()) {
			logger.debug("Setting JDBC transaction [" + txObject.getConnectionHolder().getConnection() +
					"] rollback-only");
		}
		txObject.setRollbackOnly();
	}

    // DataSourceTransactionObject.java
    public void setRollbackOnly() {
        getConnectionHolder().setRollbackOnly();
    }

二、事务提交

        1、commitTransactionAfterReturning

        和回滚一样,先判断有无事务后再做提交操作。

protected void commitTransactionAfterReturning(TransactionInfo txInfo) {
    if (txInfo != null && txInfo.hasTransaction()) {
        if (logger.isTraceEnabled()) {
            logger.trace("Completing transaction for [" + txInfo.getJoinpointIdentification() + "]");
        }
        // 提交事务
        txInfo.getTransactionManager().commit(txInfo.getTransactionStatus());
    }
}

        2、commit

        如果发现事务链中存在回滚标记则表明需要回滚,调用processRollback方法回滚,具体内容上文做过介绍了这里不再赘述。判断无需回滚后就可以进行提交了。

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()) {
        // 回滚操作
        processRollback(defStatus); 
        return;
    }
    if (!shouldCommitOnGlobalRollbackOnly() && defStatus.isGlobalRollbackOnly()) {
        }
        // 回滚操作
        processRollback(defStatus); 
        if (status.isNewTransaction() || isFailEarlyOnGlobalRollbackOnly()) {
            throw new UnexpectedRollbackException(
                "Transaction rolled back because it has been marked as rollback-only");
        }
        return;
    }
    // 提交操作
    processCommit(defStatus); 
}

        3、processCommit

        判断为独立事务则提交。

private void processCommit(DefaultTransactionStatus status) throws TransactionException {
    try {
        boolean beforeCompletionInvoked = false;
        try {
            prepareForCommit(status);
            triggerBeforeCommit(status);
            triggerBeforeCompletion(status);
            beforeCompletionInvoked = true;
            boolean globalRollbackOnly = false;
            if (status.isNewTransaction() || isFailEarlyOnGlobalRollbackOnly()) {
                globalRollbackOnly = status.isGlobalRollbackOnly();
            }
            if (status.hasSavepoint()) {
                // 释放保存点信息
                status.releaseHeldSavepoint(); 
            }
            else if (status.isNewTransaction()) {
                // 独立事务则提交
                doCommit(status); 
            }
            if (globalRollbackOnly) {
                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) {
            if (isRollbackOnCommitFailure()) {
                doRollbackOnCommitException(status, ex);
            }
            else {
                triggerAfterCompletion(status, TransactionSynchronization.STATUS_UNKNOWN);
            }
            throw ex;
        }
        catch (RuntimeException ex) {
            if (!beforeCompletionInvoked) {
                triggerBeforeCompletion(status);
            }
            // 提交异常则回滚
            doRollbackOnCommitException(status, ex); 
            throw ex;
        }
        catch (Error err) {
            if (!beforeCompletionInvoked) {
                triggerBeforeCompletion(status);
            }
            doRollbackOnCommitException(status, err);
            throw err;
        }
        try {
            triggerAfterCommit(status);
        }
        finally {
            triggerAfterCompletion(status, TransactionSynchronization.STATUS_COMMITTED);
        }
    }
    finally {
        // 清理事务信息
        cleanupAfterCompletion(status); 
    }
}

        4、doCommit

        获取数据库连接后调用底层API提交事务。

protected void doCommit(DefaultTransactionStatus status) {
    DataSourceTransactionObject txObject = (DataSourceTransactionObject) status.getTransaction();
    // 获取当前数据库连接
    Connection con = txObject.getConnectionHolder().getConnection();
    try {
        // 调用底层API提交事务
        con.commit();
    }
    catch (SQLException ex) {
        throw new TransactionSystemException("Could not commit JDBC transaction", ex);
    }
}

三、清除事务信息

        1、cleanupAfterCompletion

        清除事务信息首先将事务状态设置为完成,然后重置数据库连接信息,如果有挂起的事务则还需要将其恢复。

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.getSuspendedResources());
    }
}

        1.1、doCleanupAfterCompletion

        doCleanupAfterCompletion方法在新事务完成后调用resetConnectionAfterTransaction方法重置数据库连接信息,并判单如果是新的数据库连接则将其放回连接池。

protected void doCleanupAfterCompletion(Object transaction) {
    DataSourceTransactionObject txObject = (DataSourceTransactionObject) transaction;
    if (txObject.isNewConnectionHolder()) {
        // 将数据库连接从当前线程中解除绑定
        TransactionSynchronizationManager.unbindResource(this.dataSource);
    }
    Connection con = txObject.getConnectionHolder().getConnection();
    try {
        // 恢复数据库连接的autoCommit状态
        if (txObject.isMustRestoreAutoCommit()) {
            con.setAutoCommit(true); 
        }
        // 重置数据库连接信息,包括隔离级别、readOnly属性等
        DataSourceUtils.resetConnectionAfterTransaction(con, txObject.getPreviousIsolationLevel());
    }
    catch (Throwable ex) {
        logger.debug("Could not reset JDBC Connection after transaction", ex);
    }
    if (txObject.isNewConnectionHolder()) {
        // 如果是新的数据库连接则将数据库连接放回连接池
        DataSourceUtils.releaseConnection(con, this.dataSource);
    }
    txObject.getConnectionHolder().clear();
}

        resetConnectionAfterTransaction方法负责重置数据库连接信息,包括隔离级别、readOnly属性。 

	public static void resetConnectionAfterTransaction(Connection con, Integer previousIsolationLevel) {
		Assert.notNull(con, "No Connection specified");
		try {
			if (previousIsolationLevel != null) {
				con.setTransactionIsolation(previousIsolationLevel);
			}
			if (con.isReadOnly()) {
				con.setReadOnly(false);
			}
		}
		catch (Throwable ex) {
			logger.debug("Could not reset JDBC Connection after transaction", ex);
		}
	}

        1.2、resume

        如果事务执行前有事务挂起,那么当前事务执行结束后需要将挂起的事务恢复,挂起事务时保存了原事务信息,重置了当前事务信息,所以恢复操作就是将当前的事务信息设置为之前保存的原事务信息。

protected final void resume(Object transaction, SuspendedResourcesHolder resourcesHolder)
throws TransactionException {
    // 判断是否有事务挂起
    if (resourcesHolder != null) {
        Object suspendedResources = resourcesHolder.suspendedResources;
        if (suspendedResources != null) {
            // 恢复挂起事务
            doResume(transaction, suspendedResources);
        }
        List suspendedSynchronizations = resourcesHolder.suspendedSynchronizations;
        if (suspendedSynchronizations != null) {
            TransactionSynchronizationManager.setActualTransactionActive(resourcesHolder.wasActive);
            TransactionSynchronizationManager.setCurrentTransactionIsolationLevel(resourcesHolder.isolationLevel);
            TransactionSynchronizationManager.setCurrentTransactionReadOnly(resourcesHolder.readOnly);
            TransactionSynchronizationManager.setCurrentTransactionName(resourcesHolder.name);
            doResumeSynchronization(suspendedSynchronizations);
        }
    }
}

         doResume方法重新绑定数据库连接与挂起的事务。

	protected void doResume(Object transaction, Object suspendedResources) {
		TransactionSynchronizationManager.bindResource(this.dataSource, suspendedResources);
	}

        2、cleanupTransactionInfo

        cleanupTransactionInfo方法清除当前节点的事务信息,将旧事务节点信息通过 threadLocal 更新到当前线程。

protected void cleanupTransactionInfo(@Nullable TransactionInfo txInfo) {
    if (txInfo != null) {
        // 从当前线程的 ThreadLocal 获取上层的事务信息,将当前事务出栈,继续执行上层事务
        txInfo.restoreThreadLocalStatus();
    }
}
private void restoreThreadLocalStatus() {
    // 当前事务处理完之后,恢复上层事务上下文
    transactionInfoHolder.set(this.oldTransactionInfo);
}

你可能感兴趣的:(Spring,spring)