47--Spring @Transactional聲明式事物(四)回滾

1.引

通過(guò)前面的分析,已經(jīng)成功的創(chuàng)建了事物,但是不要忘了,當(dāng)前方法仍在方法攔截器鏈中?;仡櫼幌麓a片段:

// 2.處理聲明式事物
if (txAttr == null || !(tm instanceof CallbackPreferringPlatformTransactionManager)) {
    // Standard transaction demarcation with getTransaction and commit/rollback calls.
    // 2.2 創(chuàng)建事物
    TransactionInfo txInfo = createTransactionIfNecessary(tm, txAttr, joinpointIdentification);
    System.out.println("==創(chuàng)建了名為:["+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.
        // 2.3 繼續(xù)調(diào)用方法攔截器鏈,這里一般將會(huì)調(diào)用目標(biāo)類的方法,如:com.lyc.cn.v2.day09.AccountServiceImpl.save方法
        retVal = invocation.proceedWithInvocation();
    } catch (Throwable ex) {
        // target invocation exception
        // 2.4 如果目標(biāo)類方法拋出異常,則在此處理,例如:事物回滾
        completeTransactionAfterThrowing(txInfo, ex);
        throw ex;
    } finally {
        // 2.5 清除上一步創(chuàng)建的事物信息
        cleanupTransactionInfo(txInfo);
    }
    // 2.6 調(diào)用成功完成后執(zhí)行,但不是在異常被處理后執(zhí)行。如果我們不創(chuàng)建事務(wù),就什么也不做。
    commitTransactionAfterReturning(txInfo);
    return retVal;
}

關(guān)于攔截器鏈的調(diào)用、目標(biāo)方法的調(diào)用等不做過(guò)多的分析了,前面都有介紹。

那么接下來(lái)的處理也會(huì)分為兩種情況,有異常和無(wú)異常兩種情況,有異常的話,那么可能會(huì)執(zhí)行回滾等操作;沒(méi)有異常的話,可能會(huì)執(zhí)行事物提交等操作,接下來(lái)對(duì)兩種情況逐個(gè)分析。

2.業(yè)務(wù)方法發(fā)生異常之后的處理
/**
 * 處理異常以完成事物,基于配置該事物可能回滾也可能提交
 * Handle a throwable, completing the transaction.
 * We may commit or roll back, depending on the configuration.
 * @param txInfo information about the current transaction
 * @param ex     throwable encountered
 */
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);
        }
        // 1.回滾
        /**
         * txInfo.transactionAttribute.rollbackOn(ex)判斷回滾的條件:
         *
         * 1. 如果自定了RollbackRuleAttribute列表,如果當(dāng)前異常匹配到了RollbackRuleAttribute其中的條目,則回滾
         *    例如:可以通過(guò)rollbackFor指定觸發(fā)回滾的異常@Transactional(propagation = Propagation.REQUIRED,rollbackFor = Exception.class)
         *
         * 2. 否則如果異常是RuntimeException或者Error的類型,則回滾
         *
         * 3. 其他的異常是不會(huì)回滾的,這里要注意一下...
         */
        if (txInfo.transactionAttribute != null && txInfo.transactionAttribute.rollbackOn(ex)) {
            try {
                // 執(zhí)行回滾
                System.out.println("==準(zhǔn)備回滾"+txInfo.getJoinpointIdentification());
                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;
            }
        }
        // 2.如果未能滿足回滾條件,則有可能會(huì)提交事物,也有可能會(huì)回滾事物
        // 注意:如果TransactionStatus.isRollbackOnly()為true,則仍然會(huì)執(zhí)行回滾
        else {
            // We don't roll back on this exception.
            // Will still roll back if TransactionStatus.isRollbackOnly() is true.
            try {
                System.out.println("==準(zhǔn)備提交"+txInfo.getJoinpointIdentification());
                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;
            }
        }
    }
}
2.1 判斷回滾條件:
  1. 如果自定了RollbackRuleAttribute列表,如果當(dāng)前異常匹配到了RollbackRuleAttribute其中的條目,則回滾
    例如:可以通過(guò)rollbackFor指定觸發(fā)回滾的異常@Transactional(propagation = Propagation.REQUIRED,rollbackFor = Exception.class)。
  2. 否則如果異常是RuntimeException或者Error的類型,則回滾。
  3. 其他的異常是不會(huì)回滾的。
2.2 正?;貪L

如果滿足了if條件,那么當(dāng)前事物會(huì)執(zhí)行回滾操作

2.3 回滾或提交

如果未能滿足if條件,那么當(dāng)前事物可能會(huì)被提交、可能會(huì)被回滾。例如當(dāng)前事物的rollbackOnly為true,依然會(huì)執(zhí)行回滾操作。當(dāng)然如果未能滿足回滾條件的話,即使該事物拋出異常,依然會(huì)被提交。

接下來(lái)對(duì)正?;貪L、回滾或提交逐個(gè)分析。

3.正?;貪L
// 執(zhí)行回滾前檢查事物狀態(tài)
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);
}

// 執(zhí)行回滾
private void processRollback(DefaultTransactionStatus status, boolean unexpected) {
    try {
        boolean unexpectedRollback = unexpected;

        try {

            // 1.事物完成之前的觸發(fā)器調(diào)用
            triggerBeforeCompletion(status);

            // 2.如果有保存點(diǎn),則調(diào)用rollbackToHeldSavepoint回滾到保存點(diǎn)
            if (status.hasSavepoint()) {
                if (status.isDebug()) {
                    logger.debug("Rolling back transaction to savepoint");
                }
                status.rollbackToHeldSavepoint();
            }
            // 3.如果當(dāng)前事物是一個(gè)新的事物,則調(diào)用doRollback執(zhí)行給定事物的回滾
            else if (status.isNewTransaction()) {
                if (status.isDebug()) {
                    logger.debug("Initiating transaction rollback");
                }
                doRollback(status);
            }
            else {
                // Participating in larger transaction
                // 4.如果當(dāng)前事物并非獨(dú)立事物,則將當(dāng)前事物的rollbackOnly屬性標(biāo)記為true,等到事物鏈完成之后,一起執(zhí)行回滾

                // 如果當(dāng)前存在事物,但是
                // 事物的rollbackOnly屬性已經(jīng)被標(biāo)記為true
                // 或者globalRollbackOnParticipationFailure(返回是否僅在參與事務(wù)失敗后才將現(xiàn)有事務(wù)全局標(biāo)記為回滾)為true
                if (status.hasTransaction()) {
                    if (status.isLocalRollbackOnly() || isGlobalRollbackOnParticipationFailure()) {
                        if (status.isDebug()) {
                            logger.debug("Participating transaction failed - marking existing transaction as rollback-only");
                        }
                        System.out.println("==當(dāng)前事物并非獨(dú)立事物,且RollbackOnly為true\n");
                        // 則將ConnectionHolder中的rollbackOnly標(biāo)記為true
                        doSetRollbackOnly(status);
                    }
                    else {
                        if (status.isDebug()) {
                            logger.debug("Participating transaction failed - letting transaction originator decide on rollback");
                        }
                    }
                }
                // 如果當(dāng)前不存在事物,則不會(huì)回滾
                // 例如配置了 @Transactional(propagation = Propagation.NOT_SUPPORTED)
                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;
        }

        // 5.事物完成之后的觸發(fā)器調(diào)用
        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);
    }
}
  1. 事物完成前、完成后觸發(fā)器的調(diào)用,例如:如果我們?cè)跇I(yè)務(wù)方法里注冊(cè)了TransactionSynchronizationAdapter,那么在這里會(huì)分別執(zhí)行其beforeCompletion、afterCompletion,以完成一些額外的功能擴(kuò)展。關(guān)于TransactionSynchronizationAdapter我們留在后面講解,這里大家只需要了解會(huì)在事物完成前后調(diào)用一些額外的方法即可。
  2. 如果有保存點(diǎn),則調(diào)用rollbackToHeldSavepoint回滾到保存點(diǎn)
  3. 如果當(dāng)前事物是一個(gè)新的事物,則調(diào)用doRollback執(zhí)行給定事物的回滾
  4. 如果當(dāng)前事物并非獨(dú)立事物,則將當(dāng)前事物的rollbackOnly屬性標(biāo)記為true,等到事物鏈完成之后,一起執(zhí)行回滾
    大概的流程就是這樣了,因?yàn)槲覀兎治龅氖菃蝧ervice下的單個(gè)業(yè)務(wù)方法調(diào)用,所以這里我們還是只分析最簡(jiǎn)單的doRollback正?;貪L調(diào)用(事物嵌套留在后面講解)

關(guān)于doRollback的調(diào)用,對(duì)于不同的事物管理器分別有不同的調(diào)用方法,這里我們只看DataSourceTransactionManager的回滾方法:

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

該方法很簡(jiǎn)單,從ConnectionHolder中拿到連接,執(zhí)行回滾。這里涉及到具體的JDBC驅(qū)動(dòng)的方法調(diào)用,感興趣的同學(xué)可以自己深入分析下,就不多做介紹了。

4.回滾或提交
public final void commit(TransactionStatus status) throws TransactionException {
    // 如果當(dāng)前事物已經(jīng)被標(biāo)記為完成,拋出異常
    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;

    // 如果rollbackOnly為true,則回滾
    if (defStatus.isLocalRollbackOnly()) {
        if (defStatus.isDebug()) {
            logger.debug("Transactional code has requested rollback");
        }
        processRollback(defStatus, false);
        return;
    }

    // shouldCommitOnGlobalRollbackOnly --> 返回是否對(duì)標(biāo)記為僅以全局方式回滾的事務(wù)調(diào)用
    // defStatus.isGlobalRollbackOnly() --> 實(shí)現(xiàn)了SmartTransactionObject并且事物的rollbackOnly被標(biāo)記為true
    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);
}

關(guān)于回滾和提交的判斷條件注釋都寫(xiě)的很清晰了,對(duì)于rollbackOnly這里不做太多的介紹,留在后面的章節(jié)分析。大家只要記住,事物到了這里,可能會(huì)回滾也可能會(huì)提交即可。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

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

  • Swift1> Swift和OC的區(qū)別1.1> Swift沒(méi)有地址/指針的概念1.2> 泛型1.3> 類型嚴(yán)謹(jǐn) 對(duì)...
    cosWriter閱讀 11,681評(píng)論 1 32
  • Java繼承關(guān)系初始化順序 父類的靜態(tài)變量-->父類的靜態(tài)代碼塊-->子類的靜態(tài)變量-->子類的靜態(tài)代碼快-->父...
    第六象限閱讀 2,261評(píng)論 0 9
  • 很多人喜歡這篇文章,特此同步過(guò)來(lái) 由淺入深談?wù)搒pring事務(wù) 前言 這篇其實(shí)也要?dú)w納到《常識(shí)》系列中,但這重點(diǎn)又...
    碼農(nóng)戲碼閱讀 4,933評(píng)論 2 59
  • 這個(gè)世界聰明的人很多,但自認(rèn)為聰明而亂作主張的人也不少。 生活的意外,讓我感到恐懼…… 有時(shí)候小小一個(gè)轉(zhuǎn)身,就是一...
    逆風(fēng)追夢(mèng)人閱讀 523評(píng)論 1 2
  • 在這個(gè)多次元宇宙里,我們生活在地球,可你知道自己來(lái)自哪里嗎? 從小開(kāi)始每次抬頭仰望天空,總覺(jué)得那里的某個(gè)角落是家,...
    C佐_閱讀 345評(píng)論 0 0

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