一個關(guān)于Spring事務(wù)的坑(Transaction rolled back because it has been marked as rollback-only)

在使用Jpa的@Version的時候偶然發(fā)現(xiàn)了一個Spring事務(wù)的坑,花了一個下午的時間才成功解決,記錄一下。

@Version當(dāng)修改或刪除操作的數(shù)據(jù)版本不一致的時候會拋出一個異常:
ObjectOptimisticLockingFailureException

Service用以下代碼捕獲了這個異常,消化并拋出了一個新的自定義異常:

        try {
          //調(diào)用持久化方法拋出ObjectOptimisticLockingFailureException異常
        } catch (ObjectOptimisticLockingFailureException e) {
            if (logger.isErrorEnabled()) {
                logger.error(e.getMessage());
            }
            //捕獲處理后拋出一個新的自定義異常
            throw new SkeletonBaseException("數(shù)據(jù)版本錯誤");
        } catch (Exception e) {
            if (logger.isErrorEnabled()) {
                logger.error(e.getMessage());
            }
            throw SkeletonBaseException.getException(e, e.getMessage());
        }

但是在Controller的最終異常處理中

        try {
            //調(diào)用上面的方法
        } catch (Exception e) {
            e.printStackTrace();
            if (logger.isErrorEnabled()) {
                logger.error(e.getMessage());
            }
            responseRange.setException(e);
        }
        return responseRange;

卻得到了一個完全不一樣的異常信息:
Transaction rolled back because it has been marked as rollback-only
這個異常信息是說事務(wù)已經(jīng)回滾,因為它被標(biāo)記成了回滾。

這里就很奇怪了。
按照我的想法,這個異常信息應(yīng)該是Service拋出的自定義異常數(shù)據(jù)版本錯誤才對啊。
想了一下這就說明在Service返回Controller的過程中還發(fā)生了一個新的異常,而把我的異常頂?shù)袅恕?br> 為什么會發(fā)生這種事情?
在網(wǎng)上查了許多大神的資料,Debugger了半天Spring的源碼。
最終總結(jié)出了一個答案
Spring的事務(wù)切面認(rèn)為Service的方法沒有拋出異常,在Service結(jié)束后,打算正常Commit提交事務(wù)!但是這個事務(wù)已經(jīng)被標(biāo)記成了rollback-only狀態(tài),所以提交失敗,拋出上面出乎意料的異常信息!
異常被頂?shù)舻脑蛑懒恕?br> 但是我們Service明明最終拋出了一個自定義異常?。∷艿侥睦锶チ?!
為什么事務(wù)切面認(rèn)為沒有拋出異常呢?!
這就是坑之所在了!
我的自定義異常繼承了Exception而不是RuntimeException
因為知道Spring的事務(wù)默認(rèn)回滾是發(fā)生RuntimeException所以特地配置了rollbackFor = Exception.class
按理來說不應(yīng)該Commit而是正常rollback。
不過問題范圍縮小了,又針對性的搜索了好一陣兒。
原因:
try{}catch(){}這樣的代碼塊中,最后必須拋出一個RuntimeException,Spring事務(wù)切面才會認(rèn)為你的方法有異常出現(xiàn)!即使配置了rollbackFor = Exception.class也不管用!
解決方法:把自定義異常改成繼承RuntimeException就OK了!

?著作權(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)容