Spring事務(wù)處理機制以及錯誤使用TransactionSynchronization的afterCompletion方法引起的問題

前言

我們都知道spring有聲明式事務(wù)和編程式事務(wù),聲明式只需要提供@Transactional的注解,然后事務(wù)的開啟和提交/回滾、資源的清理就都由spring來管控,我們只需要關(guān)注業(yè)務(wù)代碼即可;而編程式事務(wù)則需要使用spring提供的模板,如TransactionTemplate,或者直接使用底層的PlatformTransactionManager。

聲明式事務(wù)的最大優(yōu)點就是對代碼的侵入性較小,只需要在方法上加@Transactional的注解就可以實現(xiàn)事務(wù);編程式事務(wù)的最大優(yōu)點就是事務(wù)的管控粒度較細,在實現(xiàn)某個代碼塊的事務(wù)。

背景

簡單介紹完spring的事務(wù)機制那就要引入這一次碰到的問題了,我相信大多數(shù)人應(yīng)該和我一樣,只要怎么使用,比如加個注解啥的,但是底層原理不清楚。好一點的知道AOP動態(tài)代理,再好一點就是知道事務(wù)的傳播機制(一般也就用用默認的REQUIRED)。真正底層的事務(wù)處理的源碼很多人應(yīng)該是沒有看過的,當然我也是沒有滴~~ 但是這一次碰到的問題讓我不得不去看源碼了。

這段時間一直在做代碼的重構(gòu),既然是重新寫的代碼,當然想寫得漂亮一點,不然是要被后人戳脊梁骨的~~ 結(jié)果所有代碼都寫完,都提測了,在測試環(huán)境卻報一個詭異的異常

java.sql.SQLException: PooledConnection has already been closed

而且這不是必現(xiàn)的,而一旦出現(xiàn),那任何涉及數(shù)據(jù)庫連接的接口都有可能報這個錯。從字面意思看是用到的數(shù)據(jù)庫連接被關(guān)閉了,但是理解不能啊,這種底層的事情不都是spring幫忙做好了么。建議測試重啟機器,心中期待不會再現(xiàn)

結(jié)果依然出現(xiàn),而且頻率還不低,都阻塞測試了。。那就只好操起久違的調(diào)試源碼的大刀,硬著頭皮上了。

過程

本次源碼使用的是spring版本是 4.2.4.RELEASE,事務(wù)管理器則是參照項目使用的DataSourceTransactionManager。

入口

  1. 首先是事務(wù)的入口,spring用的是動態(tài)代理,如果某個方法被標注了@Transactional,則會由TransactionInterceptor攔截,在原始方法的前后增加一些額外的處理??梢钥吹?,調(diào)用的是TransactionInterceptor的invoke方法,而內(nèi)部又調(diào)用了invokeWithinTransaction方法,但其實這個并不一定會創(chuàng)建事務(wù)(事務(wù)傳播機制里有幾種情況是不需要或者不支持事務(wù)的)。
public Object invoke(final MethodInvocation invocation) throws Throwable {
    // Work out the target class: may be {@code null}.
    // The TransactionAttributeSource should be passed the target class
    // as well as the method, which may be from an interface.
    Class<?> targetClass = (invocation.getThis() != null ? AopUtils.getTargetClass(invocation.getThis()) : null);

    // Adapt to TransactionAspectSupport's invokeWithinTransaction...
    return invokeWithinTransaction(invocation.getMethod(), targetClass, new InvocationCallback() {
        @Override
        public Object proceedWithInvocation() throws Throwable {
            return invocation.proceed();
        }
    });
}
  1. TransactionInterceptor繼承了TransactionAspectSupport這個抽象類,invokeWithinTransaction這個方法是在父類中的。方法里的英文注釋是源碼中的,中文注釋是我加上去的。CallbackPreferringPlatformTransactionManager是實現(xiàn)了PlatformTransactionManager接口,如果使用的事務(wù)管理器是CallbackPreferringPlatformTransactionManager的實現(xiàn),則會將事務(wù)的控制交由這個類的execute方法,這里先省略。我們來看一般情況(很多應(yīng)該用的是DataSourceTransactionManager吧),總的來說可以將這個方法分為幾部分:
protected Object invokeWithinTransaction(Method method, Class<?> targetClass, final InvocationCallback invocation) throws Throwable {
    // If the transaction attribute is null, the method is non-transactional.
    final TransactionAttribute txAttr = getTransactionAttributeSource().getTransactionAttribute(method, targetClass);
    final PlatformTransactionManager tm = determineTransactionManager(txAttr);
    final String joinpointIdentification = methodIdentification(method, targetClass);

    if (txAttr == null || !(tm instanceof CallbackPreferringPlatformTransactionManager)) {
        // Standard transaction demarcation with getTransaction and commit/rollback calls.
        // 如果當前方法需要事務(wù)則會創(chuàng)建事務(wù)(@Transactional不代表一定創(chuàng)建事務(wù),可以看spring的事務(wù)傳播機制)
        TransactionInfo txInfo = createTransactionIfNecessary(tm, txAttr, joinpointIdentification);
        Object retVal = null;
        try {
            // This is an around advice: Invoke the next interceptor in the chain.
            // This will normally result in a target object being invoked.
            // 可以將這個方法視為調(diào)用真正的業(yè)務(wù)方法(其實內(nèi)部還有一些攔截器的處理)
            retVal = invocation.proceedWithInvocation();
        }
        catch (Throwable ex) {
            // target invocation exception
            // 事務(wù)拋出異常的時候的處理
            completeTransactionAfterThrowing(txInfo, ex);
            throw ex;
        }
        finally {
            // 只做一件事,就是把事務(wù)的上下文信息改回本事務(wù)開始之前的上下文
            // 因為有可能本事務(wù)是被包裹在其他事務(wù)中的,可以看spring的事務(wù)傳播機制
            cleanupTransactionInfo(txInfo);
        }
        // 事務(wù)執(zhí)行成功后將事務(wù)的狀態(tài)信息提交
        commitTransactionAfterReturning(txInfo);
        return retVal;
    }

    else {
        // It's a CallbackPreferringPlatformTransactionManager: pass a TransactionCallback in.
        // 省略
    }
}

createTransactionIfNecessary

這里要知道幾個類的含義:

  • TransactionAttribute 事務(wù)的屬性,比如我們在@Transactional里面的一些定義,使用的事務(wù)管理器、事務(wù)隔離級別、超時時間等
  • TransactionStatus 事務(wù)的運行時狀態(tài),如是否已完成等
  • TransactionInfo 事務(wù)信息的聚合,包含了事務(wù)屬性、事務(wù)狀態(tài)、事務(wù)管理器、被事務(wù)包裹的方法定義信息、本事務(wù)執(zhí)行前的外層事務(wù)信息等

這里復(fù)雜的是獲取事務(wù)的方法,其他方法做的事情見我的中文注釋。

protected TransactionInfo createTransactionIfNecessary(PlatformTransactionManager tm, TransactionAttribute txAttr, final String joinpointIdentification) {
    // If no name specified, apply method identification as transaction name.
    // 新建一個TransactionAttribute的代理對象,其實用的是裝飾器模式
    if (txAttr != null && txAttr.getName() == null) {
        txAttr = new DelegatingTransactionAttribute(txAttr) {
            @Override
            public String getName() {
                return joinpointIdentification;
            }
        };
    }

    TransactionStatus status = null;
    if (txAttr != null) {
        if (tm != null) {
            // 這里會根據(jù)事務(wù)管理器獲取事務(wù)對象
            status = tm.getTransaction(txAttr);
        }
        else {
            if (logger.isDebugEnabled()) {
                logger.debug("Skipping transactional joinpoint [" + joinpointIdentification +
                        "] because no transaction manager has been configured");
            }
        }
    }
    // 將事務(wù)信息聚合然后返回,這里會有一個事務(wù)信息綁定到當前線程的操作(外層事務(wù)信息會存下來)
    return prepareTransactionInfo(tm, txAttr, joinpointIdentification, status);
}
  1. getTransaction方法
  • TransactionSynchronizationManager 以threadLocal的方式保存當前線程事務(wù)相關(guān)信息的對象

這里省略了一些不重要的流程,重點是doBegin方法,這里會開啟事務(wù)

public final TransactionStatus getTransaction(TransactionDefinition definition) throws TransactionException {
    // 會新建一個事務(wù)對象,從TransactionSynchronizationManager中獲取當前線程持有的數(shù)據(jù)庫連接的句柄
    //如果是最開始的事務(wù),這個句柄是會為null的,如果是內(nèi)層事務(wù),則會復(fù)用連接
    Object transaction = doGetTransaction();

    // 省略
    
    // 當前線程關(guān)聯(lián)的數(shù)據(jù)庫連接存在且事務(wù)處于激活狀態(tài),那么當前事務(wù)會根據(jù)事務(wù)傳播機制來處理當前事務(wù)
    if (isExistingTransaction(transaction)) {
        // Existing transaction found -> check propagation behavior to find out how to behave.
        return handleExistingTransaction(definition, transaction, debugEnabled);
    }

    // 省略

    // No existing transaction found -> check propagation behavior to find out how to proceed.
    if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_MANDATORY) {
        // 省略
    }
    else if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRED ||
            definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRES_NEW ||
            definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NESTED) {
        SuspendedResourcesHolder suspendedResources = suspend(null);
        if (debugEnabled) {
            logger.debug("Creating new transaction with name [" + definition.getName() + "]: " + definition);
        }
        try {
            boolean newSynchronization = (getTransactionSynchronization() != SYNCHRONIZATION_NEVER);
            DefaultTransactionStatus status = newTransactionStatus(
                    definition, transaction, true, newSynchronization, debugEnabled, suspendedResources);
            // 開啟事務(wù)
            doBegin(transaction, definition);
            // 將事務(wù)信息綁定到當前線程(存在TransactionSynchronizationManager的threadLocal中)
            prepareSynchronization(status, definition);
            return status;
        }
        catch (RuntimeException ex) {
            resume(null, suspendedResources);
            throw ex;
        }
        catch (Error err) {
            resume(null, suspendedResources);
            throw err;
        }
    }
    else {
        // Create "empty" transaction: no actual transaction, but potentially synchronization.
        // 這里就是前面說的不需要事務(wù)的情況
        if (definition.getIsolationLevel() != TransactionDefinition.ISOLATION_DEFAULT && logger.isWarnEnabled()) {
            logger.warn("Custom isolation level specified but no actual transaction initiated; " +
                    "isolation level will effectively be ignored: " + definition);
        }
        boolean newSynchronization = (getTransactionSynchronization() == SYNCHRONIZATION_ALWAYS);
        return prepareTransactionStatus(definition, null, true, newSynchronization, debugEnabled, null);
    }
}
  1. doBegin方法
protected void doBegin(Object transaction, TransactionDefinition definition) {
    DataSourceTransactionObject txObject = (DataSourceTransactionObject) transaction;
    Connection con = null;

    try {
        if (txObject.getConnectionHolder() == null ||
                txObject.getConnectionHolder().isSynchronizedWithTransaction()) {
            // 從dataSource從獲取一個連接,如果使用了連接池,則會從連接池中獲取
            Connection newCon = this.dataSource.getConnection();
            if (logger.isDebugEnabled()) {
                logger.debug("Acquired Connection [" + newCon + "] for JDBC transaction");
            }
            // 事務(wù)對象設(shè)置連接的句柄
            txObject.setConnectionHolder(new ConnectionHolder(newCon), true);
        }

        txObject.getConnectionHolder().setSynchronizedWithTransaction(true);
        con = txObject.getConnectionHolder().getConnection();

        Integer previousIsolationLevel = DataSourceUtils.prepareConnectionForTransaction(con, definition);
        txObject.setPreviousIsolationLevel(previousIsolationLevel);

        // Switch to manual commit if necessary. This is very expensive in some JDBC drivers,
        // so we don't want to do it unnecessarily (for example if we've explicitly
        // configured the connection pool to set it already).
        // 省略

        // Bind the session holder to the thread.
        // 綁定數(shù)據(jù)庫連接到當前線程,這里的key是dataSource,所以如果事務(wù)中換了dataSource那事務(wù)就不生效了
        if (txObject.isNewConnectionHolder()) {
            TransactionSynchronizationManager.bindResource(getDataSource(), txObject.getConnectionHolder());
        }
    }

    catch (Throwable ex) {
        // 釋放連接回連接池
        // 當前線程持有的連接句柄也一并釋放
    }
}

到此為止,事務(wù)的信息全部準備好了,事務(wù)也開啟了,這個時候業(yè)務(wù)方法就是在事務(wù)中執(zhí)行了(如果配置了需要事務(wù)的話)

<-------------------------------我是罪惡的分割線------------------------------------>

事務(wù)執(zhí)行完畢是需要進行資源的清理和釋放的,spring在開啟事務(wù)的時候綁定了很多信息到線程中,而現(xiàn)在的應(yīng)用出于資源和性能的考慮,基本用的都是連接池和線程池,會有復(fù)用的可能性,如果資源的釋放或者清理不到位,會有莫名其妙的問題出現(xiàn)(我這一次的問題就是這么來的。。。當然不是框架的問題,是自己操作有誤)。

commitTransactionAfterReturning

這個方法是在業(yè)務(wù)方法正常返回后執(zhí)行的,如果當前是存在事務(wù)的,則會調(diào)用事務(wù)管理器的commit方法

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());
    }
}
  1. commit方法

這里會有一些標志位的檢測,如果設(shè)置為true,那事務(wù)是不會提交的,會回滾。比如單元測試的時候不管什么情況我們都不想提交,spring就是靠這個標志位實現(xiàn)的。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()) {
        if (defStatus.isDebug()) {
            logger.debug("Transactional code has requested rollback");
        }
        processRollback(defStatus);
        return;
    }
    if (!shouldCommitOnGlobalRollbackOnly() && defStatus.isGlobalRollbackOnly()) {
        if (defStatus.isDebug()) {
            logger.debug("Global transaction is marked as rollback-only but transactional code requested commit");
        }
        processRollback(defStatus);
        // Throw UnexpectedRollbackException only at outermost transaction boundary
        // or if explicitly asked to.
        if (status.isNewTransaction() || isFailEarlyOnGlobalRollbackOnly()) {
            throw new UnexpectedRollbackException(
                    "Transaction rolled back because it has been marked as rollback-only");
        }
        return;
    }

    processCommit(defStatus);
}

題外話

還記得TransactionSynchronizationManager這個類嗎?里面維護了當前線程的一些信息,其中有一個就是TransactionSynchronization的列表,我們可以自定義實現(xiàn)一個TransactionSynchronization然后在事務(wù)中綁定到當前線程,這樣可以實現(xiàn)在事務(wù)提交前或者提交后或者完成后執(zhí)行一些我們自定義的操作。這次出現(xiàn)的問題就是因為我們業(yè)務(wù)代碼里有自定義實現(xiàn)的TransactionSynchronization,至于具體原因后面再詳述。

  1. processCommit方法

各個方法做的事見中文注釋,這里要注意cleanupAfterCompletion方法,會去清理相關(guān)的資源。

private void processCommit(DefaultTransactionStatus status) throws TransactionException {
    try {
        boolean beforeCompletionInvoked = false;
        try {
            prepareForCommit(status);
            // 調(diào)用當前線程的TransactionSynchronization列表的對應(yīng)方法
            // 這里注意,beforeCompletion方法的異常是會被吞掉的,beforeCommit的異常則會傳播出去
            triggerBeforeCommit(status);
            triggerBeforeCompletion(status);
            beforeCompletionInvoked = true;
            boolean globalRollbackOnly = false;
            if (status.isNewTransaction() || isFailEarlyOnGlobalRollbackOnly()) {
                globalRollbackOnly = status.isGlobalRollbackOnly();
            }
            //如果用到了spring的NESTED傳播,底層用到了數(shù)據(jù)庫的savePoint,所以這里會釋放
            if (status.hasSavepoint()) {
                if (status.isDebug()) {
                    logger.debug("Releasing transaction savepoint");
                }
                status.releaseHeldSavepoint();
            }
            // 只有最外層的事務(wù)這里才是true
            else if (status.isNewTransaction()) {
                if (status.isDebug()) {
                    logger.debug("Initiating transaction commit");
                }
                // 這里調(diào)用底層數(shù)據(jù)庫連接的commit方法提交事務(wù)
                doCommit(status);
            }
            // Throw UnexpectedRollbackException if we have a global rollback-only
            // marker but still didn't get a corresponding exception from commit.
            if (globalRollbackOnly) {
                throw new UnexpectedRollbackException(
                        "Transaction silently rolled back because it has been marked as rollback-only");
            }
        }
        catch (UnexpectedRollbackException ex) {
            // can only be caused by doCommit
            // 這里調(diào)用TransactionSynchronization列表的afterCompletion方法,會吞掉異常
            triggerAfterCompletion(status, TransactionSynchronization.STATUS_ROLLED_BACK);
            throw ex;
        }
        catch (TransactionException ex) {
            // can only be caused by doCommit
            if (isRollbackOnCommitFailure()) {
                // 如果提交異常這里會回滾事務(wù),里層也是調(diào)用TransactionSynchronization列表的afterCompletion方法
                // 只不過如果回滾失敗,事務(wù)狀態(tài)就是未知
                doRollbackOnCommitException(status, ex);
            }
            else {
                // 單純調(diào)用TransactionSynchronization列表的afterCompletion方法
                triggerAfterCompletion(status, TransactionSynchronization.STATUS_UNKNOWN);
            }
            throw ex;
        }
        catch (RuntimeException ex) {
            if (!beforeCompletionInvoked) {
                // 如果前面beforeCompletion未調(diào)用,則這里調(diào)一次
                triggerBeforeCompletion(status);
            }
            // 回滾事務(wù)
            doRollbackOnCommitException(status, ex);
            throw ex;
        }
        catch (Error err) {
            if (!beforeCompletionInvoked) {
                triggerBeforeCompletion(status);
            }
            doRollbackOnCommitException(status, err);
            throw err;
        }

        // Trigger afterCommit callbacks, with an exception thrown there
        // propagated to callers but the transaction still considered as committed.
        try {
            // 調(diào)用TransactionSynchronization列表的afterCommit方法
            triggerAfterCommit(status);
        }
        finally {
            triggerAfterCompletion(status, TransactionSynchronization.STATUS_COMMITTED);
        }

    }
    finally {
        cleanupAfterCompletion(status);
    }
}
  1. cleanupAfterCompletion方法

這里關(guān)于資源的清理和釋放操作比較多,稍有不慎,萬劫不復(fù)啊。。。

private void cleanupAfterCompletion(DefaultTransactionStatus status) {
    // 將事務(wù)狀態(tài)設(shè)為已完成
    status.setCompleted();
    // 最外層事務(wù)會去清理線程綁定的資源,包含TransactionSynchronization列表
    if (status.isNewSynchronization()) {
        TransactionSynchronizationManager.clear();
    }
    if (status.isNewTransaction()) {
        // 從當前線程綁定的資源中移除數(shù)據(jù)庫連接句柄
        // 將連接的一些屬性重置,恢復(fù)默認值
        // 將連接還給連接池(如果沒用連接池會直接關(guān)閉連接)
        // 解除事務(wù)與連接的綁定關(guān)系
        doCleanupAfterCompletion(status.getTransaction());
    }
    // 用于事務(wù)的掛起和恢復(fù),這里先略過
    if (status.getSuspendedResources() != null) {
        if (status.isDebug()) {
            logger.debug("Resuming suspended transaction after completion of inner transaction");
        }
        resume(status.getTransaction(), (SuspendedResourcesHolder) status.getSuspendedResources());
    }
}

completeTransactionAfterThrowing

業(yè)務(wù)方法拋出異常后會執(zhí)行本方法,主要就是事務(wù)的回滾以及定義的TransactionSynchronization列表的關(guān)聯(lián)事務(wù)方法的執(zhí)行,上面有提到,這里就不詳述了。

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 {
                // 里層調(diào)用的就是processRollback方法
                txInfo.getTransactionManager().rollback(txInfo.getTransactionStatus());
            }
            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 {
            // We don't roll back on this exception.
            // Will still roll back if TransactionStatus.isRollbackOnly() is true.
            try {
                // 如果拋出的異常不屬于回滾異常范圍內(nèi),則事務(wù)依然提交
                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;
            }
        }
    }
}
  1. processRollback方法

這里很多方法前面都有提到,不詳述了。

private void processRollback(DefaultTransactionStatus status) {
    try {
        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");
                }
                // 調(diào)用數(shù)據(jù)庫連接的回滾方法
                doRollback(status);
            }
            else 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");
            }
        }
        catch (RuntimeException ex) {
            triggerAfterCompletion(status, TransactionSynchronization.STATUS_UNKNOWN);
            throw ex;
        }
        catch (Error err) {
            triggerAfterCompletion(status, TransactionSynchronization.STATUS_UNKNOWN);
            throw err;
        }
        triggerAfterCompletion(status, TransactionSynchronization.STATUS_ROLLED_BACK);
    }
    finally {
        cleanupAfterCompletion(status);
    }
}

總結(jié)

到此為止,spring關(guān)于事務(wù)的處理的源碼差不多分析完了,回到正題,為啥會出現(xiàn)連接已關(guān)閉的情況呢?因為我們自定義了一個TransactionSynchronization來實現(xiàn)事務(wù)事件觸發(fā)機制,并且在TransactionSynchronization的afterCompletion方法中操作了Dao層,也就是用到了數(shù)據(jù)庫連接??匆幌耡fterCompletion方法的注釋,里面有提到這個時候事務(wù)已經(jīng)提交或者回滾了,但是相關(guān)資源可能還沒有釋放,所以一旦有與數(shù)據(jù)庫連接相關(guān)的代碼,可能會參與到前面的事務(wù)中去。如果這里非要執(zhí)行與數(shù)據(jù)庫連接相關(guān)的操作,spring建議明確標注,并且使用新開事務(wù)的傳播機制??蚣芊庋b好,使用需謹慎啊。

/**
 * Invoked after transaction commit/rollback.
 * Can perform resource cleanup <i>after</i> transaction completion.
 * <p><b>NOTE:</b> The transaction will have been committed or rolled back already,
 * but the transactional resources might still be active and accessible. As a
 * consequence, any data access code triggered at this point will still "participate"
 * in the original transaction, allowing to perform some cleanup (with no commit
 * following anymore!), unless it explicitly declares that it needs to run in a
 * separate transaction. Hence: <b>Use {@code PROPAGATION_REQUIRES_NEW}
 * for any transactional operation that is called from here.</b>
 * @param status completion status according to the {@code STATUS_*} constants
 * @throws RuntimeException in case of errors; will be <b>logged but not propagated</b>
 * (note: do not throw TransactionException subclasses here!)
 * @see #STATUS_COMMITTED
 * @see #STATUS_ROLLED_BACK
 * @see #STATUS_UNKNOWN
 * @see #beforeCompletion
 */
void afterCompletion(int status);
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容