Spring事务简要分析

文章详细阐述了数据库事务的四个隔离级别,包括脏读、不可重复读和幻读的概念,并介绍了Spring中事务的传播行为。通过TransactionAspectSupport类的代码分析,展示了Spring如何处理事务的开始、提交、回滚以及异常处理。同时,提到了TransactionInfo对象在事务管理中的作用,以及在异常和正常流程中对事务状态的判断与操作。

一、事务的关键对象

事务隔离级别

脏读:

所谓脏读就是一个事务 A 读取另一个事务 B 修改但尚未提交的数据, 并在此基础上操作,而事务 B 又执行事务回滚(也就是撤销了事务), 因为这个数据是还没有提交的数据, 那么事务A读到的这个数据是脏数据,依据脏数据所做的操作可能是不正确的。

不可重复读:

所谓不可重复读就是一个事务对同一行数据重复读取两次,但是却得到了不同的结果。 在这个事务还没有结束时,另外一个事务也访问该同一数据并进行了修改。 那么,在第一个事务中的两次读此数据之间,由于第二个事务的修改,那么第一个事务两次读到的数据可能是不一样的。这样就发生了在一个事务内两次读到的数据是不一样的,因此称为是不可重复读。

幻读:

指当事务不是独立执行时发生的一种现象,例如第一个事务对一个表中所有数据的某一列进行修改,同时,第二个事务也修改这个表中的数据, 向表中插入一行新数据。那么,第一个事务的用户修改后查询, 发现表中还有没有修改的数据行(第二个事务插入的新数据),就好象发生了幻觉一样。

1、ISOLATION_DEFAULT: 这是PlatformTransactionManager默认的隔离级别,使用数据库默认的事务隔离级别。另外四个与JDBC的隔离级别相对应。

2、ISOLATION_READ_UNCOMMITTED: 这是事务最低的隔离级别,隔离级别最差,脏读、不可重复读、幻读都不能避免。

3、ISOLATION_READ_COMMITTED: 保证一个事务修改的数据提交后才能被另一个事务读取。另一个事务不能读取该事务未提交的数据。可避免脏读,不可重复读、幻读无法避免。

不可重复读原因:A事务修改,B事务查询,A事务提交前和提交后,B事务看到的数据是不一致的。

幻读原因:A事务修改,B事务新增,B事务提交前,A事务已经提交。B事务提交后,A查询发现仍有数据未修改。

4、ISOLATION_REPEATABLE_READ: 这种隔离级别可以防止脏读,不可重复读。但是可能出现幻读。(MySQL默认的隔离级别)

5、ISOLATION_SERIALIZABLE: 这是花费最高代价但是最可靠的事务隔离级别。事务顺序执行,可避免脏读、不可重复读、幻读,但效率最差。因为A事务执行时,其他事务必须等待。

总结:

自上而下, 四种隔离级别由低到高(读未提交最低, 序列化最大), 隔离级别越高,越能保证数据的完整性和一致性,但是对并发性能的影响也越大。

对于多数应用程序,可以优先考虑把数据库系统的隔离级别设为 RC(读提交)。它能够避免脏读取,而且具有较好的并发性能。尽管它会导致不可重复读、幻读和丢失更新这些并发性问题,在可能出现这类问题的个别场合,可以由应用程序采用悲观锁或乐观锁来控制。

大多数数据库的默认级别就是 RC(授权读取、读提交),比如Sql Server , Oracle。MySQL的默认隔离级别就是 RR。

事务的传播行为(Propagation Behavior)

事务的传播行为(Propagation Behavior)是一个什么概念?简单点说就是当一个事务方法被另一个事务方法调用时,传播行为可以控制是否需要创建事务以及如何创建事务,spring-tx 中定义了7种事务传播行为:

1、REQUIRED(默认) 表示方法必须运行在事务中,如果当前事务存在,方法将会在该事务中运行,否则将开启一个新的独立事务。

2、REQUIRES_NEW 创建一个新事务,如果存在当前事务,则挂起该事务

3、NESTED 嵌套事务传播类型,如果当前事务存在,则在嵌套事务中执行,否则开启一个事务

4、NOT_SUPPORTED 以非事务方式执行,如果当前存在事务,则挂起当前事务。

5、SUPPORTS 当前存在事务,则加入当前事务,如果当前没有事务,就以非事务方法执行。

6、NEVER 如果当前没有事务存在,就以非事务方式执行;如果有,就抛出异常。

7、MANDATORY 当前存在事务,则加入当前事务;如果当前事务不存在,则抛出异常。

二、核心事务底层封装实现方法 TransactionAspectSupport

@Nullable
protected Object invokeWithinTransaction(Method method, @Nullable Class<?> targetClass, final InvocationCallback invocation) throws Throwable {
    // If the transaction attribute is null, the method is non-transactional.
    // 先去获取事务的属性
    TransactionAttributeSource tas = getTransactionAttributeSource();
    final TransactionAttribute txAttr = (tas != null ? tas.getTransactionAttribute(method, targetClass) : null);
    final TransactionManager tm = determineTransactionManager(txAttr);
    if (this.reactiveAdapterRegistry != null && tm instanceof ReactiveTransactionManager){...}
	
    // 从属性中获取事务管理器, 使用mybatis的话是DataSourceTransactionManager
    PlatformTransactionManager ptm = asPlatformTransactionManager(tm);
    final String joinpointIdentification = methodIdentification(method, targetClass, txAttr);

    // 这里执行声明式事务
    if (txAttr == null || !(ptm instanceof CallbackPreferringPlatformTransactionManager)) {
        // Standard transaction demarcation with getTransaction and commit/rollback calls.
        // 创建一个带有事务信息的对象TransactionInfo
        TransactionInfo txInfo = createTransactionIfNecessary(ptm, txAttr, joinpointIdentification);
        Object retVal;
        try {
            // This is an around advice: Invoke the next interceptor in the chain.
            // This will normally result in a target object being invoked.
            // 执行原方法
            retVal = invocation.proceedWithInvocation();
        }
        catch (Throwable ex) {
            // target invocation exception
            // 如果有异常就走这里,对异常进行处理
            completeTransactionAfterThrowing(txInfo, ex);
            throw ex;
        }
        finally {
            // 如果Info里存在旧的Info,将旧的Info放入当前线程表示将当前Info变为上一个Info
            cleanupTransactionInfo(txInfo);
        }
        if (retVal != null && vavrPresent && VavrDelegate.isVavrTry(retVal)) {
            // Set rollback-only in case of Vavr failure matching our rollback rules...
            TransactionStatus status = txInfo.getTransactionStatus();
            if (status != null && txAttr != null) {
                retVal = VavrDelegate.evaluateTryFailure(retVal, txAttr, status);
            }
        }
        // 如果程序没有发生异常,就会走到这里,执行提交的操作
        commitTransactionAfterReturning(txInfo);
        return retVal;
    }
    // 这里执行编程式事务
    else {...}
}

TransactionInfo 是存放事务信息的对象, 这个对象里面有一个属性status, 这个对象描述了事务的状态,是一个新事务还是一个旧的事务。

处理事务异常的情况 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);
        }
        // 这里会判断该异常是否回滚,如果没设置的话将默认回滚RuntimeException
        // 如果是Exception这里不会回滚的,除非设置了rollbackFor->Exception
        if (txInfo.transactionAttribute != null && txInfo.transactionAttribute.rollbackOn(ex)) {
            // 如果该异常需要回滚,则回滚,这里会将Info中的status传入方法
            try {
                txInfo.getTransactionManager().rollback(txInfo.getTransactionStatus());
            }
            catch (TransactionSystemException ex2) {...}
            catch (RuntimeException | Error ex2) {...}
        }
        else {
            // We don't roll back on this exception.
            // Will still roll back if TransactionStatus.isRollbackOnly() is true.
            // 如果进入这里,说明此异常不需要进行回滚,会照常提交!这里会将Info中status传入方法
            try {
                txInfo.getTransactionManager().commit(txInfo.getTransactionStatus());
            }
            catch (TransactionSystemException ex2) {...}
            catch (RuntimeException | Error ex2) {...}
        }
    }
}

首先,我们来看下txInfo.getTransactionManager().rollback(txInfo.getTransactionStatus());

public final void rollback(TransactionStatus status) throws TransactionException {
    // 如果status已经被标记完成了,这里抛异常。
    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);
}
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();
            }
            // 如果此时的status显示是新的事务才进行回滚
            else if (status.isNewTransaction()) {
                if (status.isDebug()) {
                    logger.debug("Initiating transaction rollback");
                }
                doRollback(status);
            }
            else {
                // Participating in larger transaction
                // 如果status中有事务,进入下面
                if (status.hasTransaction()) {
                    if (status.isLocalRollbackOnly() || isGlobalRollbackOnParticipationFailure()) {
                        if (status.isDebug()) {
                            logger.debug("Participating transaction failed - marking existing transaction as rollback-only");
                        }
                        // 对status中的transaction作一个回滚了的标记
                        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 {
        // 最后完善status
        cleanupAfterCompletion(status);
    }
}

总结事务异常情况

status.hasSavepoint() 如果status中有savePoint,只回滚到savePoint

status.isNewTransaction() 如果status是一个新事务,才会真正去回滚

status.hasTransaction() 如果status有事务,将会对staus中的事务标记

只有是新的事务才会回滚。

处理事务无异常准备提交的情况 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());
    }
}
public final void commit(TransactionStatus status) throws TransactionException {
    // 如果status已经被标记完成了,这里抛异常。
    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;
    }

    // defStatus.isGlobalRollbackOnly()这个方法将判断status是否被标记回滚了
    // 主要依据是transaction中的connHolder是否被标记
    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);
}
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();
            }

            // Throw UnexpectedRollbackException if we have a global rollback-only
            // marker but still didn't get a corresponding exception from commit.
            if (unexpectedRollback) {
                throw new UnexpectedRollbackException(
                        "Transaction silently rolled back because it has been marked as rollback-only");
            }
        }
        catch (UnexpectedRollbackException ex) {...}
        catch (TransactionException ex) {...}
        catch (RuntimeException | Error ex) {...}

        // Trigger afterCommit callbacks, with an exception thrown there
        // propagated to callers but the transaction still considered as committed.
        try {
            // 触发事务提交后回调
            triggerAfterCommit(status);
        }
        finally {
            triggerAfterCompletion(status, TransactionSynchronization.STATUS_COMMITTED);
        }

    }
    finally {
        // 最后完善清理status
        cleanupAfterCompletion(status);
    }
}

总结

这里status是新事务,才会进行提交或回滚。

而无论是在异常还是没有异常的流程中,最后的finally块中都会执行一个方法

cleanupAfterCompletion(status)

private void cleanupAfterCompletion(DefaultTransactionStatus status) {
    // 设置完成标记,只能 commit/rollback 一次
    status.setCompleted();
    if (status.isNewSynchronization()) {
        TransactionSynchronizationManager.clear();
    }

    if (status.isNewTransaction()) {
        doCleanupAfterCompletion(status.getTransaction());
    }
    // 如果status中有挂起的对象 恢复 transaction
    if (status.getSuspendedResources() != null) {
        if (status.isDebug()) {
            logger.debug("Resuming suspended transaction after completion of inner transaction");
        }
        Object transaction = (status.hasTransaction() ? status.getTransaction() : null);
        resume(transaction, (SuspendedResourcesHolder) status.getSuspendedResources());
    }
}

doCleanupAfterCompletion 完善 transaction

protected void doCleanupAfterCompletion(Object transaction) {
    DataSourceTransactionObject txObject = (DataSourceTransactionObject) transaction;

    // Remove the connection holder from the thread, if exposed.
    if (txObject.isNewConnectionHolder()) {
        TransactionSynchronizationManager.unbindResource(obtainDataSource());
    }

    // Reset connection.
    Connection con = txObject.getConnectionHolder().getConnection();
    try {
        if (txObject.isMustRestoreAutoCommit()) {
            con.setAutoCommit(true);
        }
        DataSourceUtils.resetConnectionAfterTransaction(
                con, txObject.getPreviousIsolationLevel(), txObject.isReadOnly());
    }
    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();
}

txObject.isNewConnectionHolder() 如果transaction是一个新的holder,将此holder从当前线程解绑!

这里需要了解一个关键线程类,专门存放线程级别的变量TransactionSynchronizationManager

status.getSuspendedResources() != null 如果status中有挂起的对象 恢复 transaction

protected final void resume(@Nullable Object transaction, @Nullable 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(resourcesHolder.wasActive);
            TransactionSynchronizationManager.setCurrentTransactionIsolationLevel(resourcesHolder.isolationLevel);
            TransactionSynchronizationManager.setCurrentTransactionReadOnly(resourcesHolder.readOnly);
            TransactionSynchronizationManager.setCurrentTransactionName(resourcesHolder.name);
            doResumeSynchronization(suspendedSynchronizations);
        }
    }
}
@Override
protected void doResume(@Nullable Object transaction, Object suspendedResources) {
    TransactionSynchronizationManager.bindResource(obtainDataSource(), suspendedResources);
}

这里恢复只是把suspendedResources重新绑定到线程中。

1、绑定与解绑围绕一个线程变量,此变量在TransactionSynchronizationManager类中resources, 线程变量就是一个Map这个key是dataSource, 这个value是holder。

2、这里绑定与解绑只需要知道,只是在TransactionSynchronizationManager类中线程变量Map中进行添加与移除的操作而已

总结

事务的大致流程

创建事务Info对象,下面几个步骤都要围绕事务Info对象展开

1、如有异常,根据Info中status状态判断回滚或提交或其他操作

2、如无异常,准备提交也要根据Info中status进行判断正常提交还是会回滚

3、流程结束,可知无论是有异常还是无异常都会进入完善收尾工作,在收尾过程如果是新的Holder将会进行解绑。如果status中存在被挂起的事务,会恢复它,具体恢复其实就是解绑操作。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值