Spring中@Transactional注解到底做了什么(六)-----@Transactional回滾事務(wù)

回到上一章的invokeWithinTransaction方法,我們來看看事物的回滾方法completeTransactionAfterThrowing

protected void completeTransactionAfterThrowing(@Nullable TransactionInfo txInfo, Throwable ex) {
        //當(dāng)拋出異常時(shí)首先判斷當(dāng)前是否存在事務(wù)
        if (txInfo != null && txInfo.getTransactionStatus() != null) {
            if (logger.isTraceEnabled()) {
                logger.trace("Completing transaction for [" + txInfo.getJoinpointIdentification() +
                        "] after exception: " + ex);
            }
            //rollbackOn (ex instanceof RuntimeException || ex instanceof Error);
            //判斷異常類型
            if (txInfo.transactionAttribute != null && txInfo.transactionAttribute.rollbackOn(ex)) {
                try {
                    //根據(jù) TransactionStatus 信息進(jìn)行回滾處理
                    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 {
                // We don't roll back on this exception.
                // Will still roll back if TransactionStatus.isRollbackOnly() is true.
                //如果不滿足回滾條件即使拋出異常也同樣會提交
                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;
                }
            }
        }
    }

這里看一個(gè)rollbackOn方法行冰,這個(gè)方法用來判斷異常類型
默認(rèn)情況下Spring中的亊務(wù)異常處理機(jī)制只對RuntimeException和Error兩種情況感興趣肛走,我們可以利用注解方式來改變葫男,例如:
@Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)

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

然后看看rollback方法

    @Override
    public final void rollback(TransactionStatus status) throws TransactionException {
        //如果事務(wù)已經(jīng)完成.那么同次回滾會拋出異常
        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);

                //如果有保存點(diǎn) 當(dāng)前事務(wù)為單獨(dú)的線程則會退到保存點(diǎn)
                if (status.hasSavepoint()) {
                    if (status.isDebug()) {
                        logger.debug("Rolling back transaction to savepoint");
                    }
                    status.rollbackToHeldSavepoint();
                }
                //如果當(dāng)前事務(wù)為獨(dú)立的新事務(wù) 鱼响,則直接回退
                else if (status.isNewTransaction()) {
                    if (status.isDebug()) {
                        logger.debug("Initiating transaction rollback");
                    }
                    doRollback(status);
                }
                else {
                    // Participating in larger transaction
                    //如果不是獨(dú)立的事務(wù),那么只標(biāo)記狀態(tài),等到事務(wù)鏈執(zhí)行完畢后統(tǒng)一回滾
                    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 {
            //清空記錄 并將掛起的資源恢復(fù)
            cleanupAfterCompletion(status);
        }
    }

首先直接看下doRollback方法

    @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 {
            //數(shù)據(jù)庫的回滾方法
            con.rollback();
        }
        catch (SQLException ex) {
            throw new TransactionSystemException("Could not roll back JDBC transaction", ex);
        }
    }

可以看到事務(wù)的回滾,其實(shí)底層還是依靠的數(shù)據(jù)庫的回滾操作

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市振惰,隨后出現(xiàn)的幾起案子冠蒋,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 211,743評論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件加叁,死亡現(xiàn)場離奇詭異,居然都是意外死亡愈污,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,296評論 3 385
  • 文/潘曉璐 我一進(jìn)店門姑廉,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人扔涧,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 157,285評論 0 348
  • 文/不壞的土叔 我叫張陵楞遏,是天一觀的道長寡喝。 經(jīng)常有香客問我糙俗,道長,這世上最難降的妖魔是什么预鬓? 我笑而不...
    開封第一講書人閱讀 56,485評論 1 283
  • 正文 為了忘掉前任臼节,我火速辦了婚禮,結(jié)果婚禮上珊皿,老公的妹妹穿的比我還像新娘网缝。我一直安慰自己,他們只是感情好蟋定,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,581評論 6 386
  • 文/花漫 我一把揭開白布粉臊。 她就那樣靜靜地躺著,像睡著了一般驶兜。 火紅的嫁衣襯著肌膚如雪扼仲。 梳的紋絲不亂的頭發(fā)上远寸,一...
    開封第一講書人閱讀 49,821評論 1 290
  • 那天,我揣著相機(jī)與錄音屠凶,去河邊找鬼驰后。 笑死,一個(gè)胖子當(dāng)著我的面吹牛矗愧,可吹牛的內(nèi)容都是我干的灶芝。 我是一名探鬼主播,決...
    沈念sama閱讀 38,960評論 3 408
  • 文/蒼蘭香墨 我猛地睜開眼唉韭,長吁一口氣:“原來是場噩夢啊……” “哼夜涕!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起属愤,我...
    開封第一講書人閱讀 37,719評論 0 266
  • 序言:老撾萬榮一對情侶失蹤女器,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后住诸,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體驾胆,經(jīng)...
    沈念sama閱讀 44,186評論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,516評論 2 327
  • 正文 我和宋清朗相戀三年贱呐,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了丧诺。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,650評論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡吼句,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出事格,到底是詐尸還是另有隱情惕艳,我是刑警寧澤,帶...
    沈念sama閱讀 34,329評論 4 330
  • 正文 年R本政府宣布驹愚,位于F島的核電站远搪,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏逢捺。R本人自食惡果不足惜谁鳍,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,936評論 3 313
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望劫瞳。 院中可真熱鬧倘潜,春花似錦、人聲如沸志于。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,757評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽伺绽。三九已至养泡,卻和暖如春嗜湃,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背澜掩。 一陣腳步聲響...
    開封第一講書人閱讀 31,991評論 1 266
  • 我被黑心中介騙來泰國打工购披, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人肩榕。 一個(gè)月前我還...
    沈念sama閱讀 46,370評論 2 360
  • 正文 我出身青樓刚陡,卻偏偏與公主長得像,于是被迫代替她去往敵國和親点把。 傳聞我的和親對象是個(gè)殘疾皇子橘荠,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,527評論 2 349

推薦閱讀更多精彩內(nèi)容