Spring聲明式事務(wù)為何不回滾

博客原文

疑問,確實像往常一樣在service上添加了注解 @Transactional,為什么查詢數(shù)據(jù)庫時還是發(fā)現(xiàn)有數(shù)據(jù)不一致的情況,想想肯定是事務(wù)沒起作用,出現(xiàn)異常的時候數(shù)據(jù)沒有回滾。于是就對相關(guān)代碼進行了一番測試,結(jié)果發(fā)現(xiàn)一下踩進了兩個坑,確實是事務(wù)未回滾導(dǎo)致的數(shù)據(jù)不一致。下面總結(jié)一下經(jīng)驗教訓(xùn):

Spring事務(wù)的管理操作方法

下面先總結(jié)一下Spring的事務(wù)管理方式,spring支持兩種事務(wù)管理的操作方式,編程式的和聲明式的(xml或者注解)。

  • 編程式的事務(wù)管理
  • 實際應(yīng)用中很少使用
  • 通過使用TransactionTemplate 手動管理事務(wù)
  • 聲明式的事務(wù)管理
  • 開發(fā)中推薦使用(代碼侵入最少)
  • Spring的聲明式事務(wù)是通過AOP實現(xiàn)的

主要掌握聲明式的事務(wù)管理。

spring事務(wù)不回滾的兩個原因

總結(jié)一下導(dǎo)致事務(wù)不回滾的兩個原因,一是Service類內(nèi)部方法調(diào)用,二是try...catch異常。

1. Service類內(nèi)部方法調(diào)用

大概就是 Service 中有一個方法 A,會內(nèi)部調(diào)用方法 B, 方法 A 沒有事務(wù)管理,方法 B 采用了聲明式事務(wù),通過在方法上聲明 Transactional 的注解來做事務(wù)管理。示例代碼如下:

@Service
public class RabbitServiceImpl implements RabbitService {

    @Autowired
    private RabbitDao rabbitDao;
    @Autowired
    private TortoiseDao tortoiseDao;
    
    @Override
    public Rabbit methodA(String name){
        return methodB(name);
    }
    
    @Transactional(propagation = Propagation.REQUIRED, isolation = Isolation.READ_COMMITTED)
    public boolean methodB(String name){
        rabbitDao.insertRabbit(name);
        tortoiseDao.insertTortoise(name);
        return true;
    }
    
}

單元測試代碼如下:

public class RabbitServiceImplTest {

    @Autowired
    private RabbitService rabbitService;
    
    // 事務(wù)未開啟
    @Test
    public void testA(){
        rabbitService.methodA("rabbit");
    }
    
    // 事務(wù)開啟
    @Test
    public void testB(){
        rabbitService.methodB("rabbit");
    }
}

從上一節(jié)中可以看到,聲明式事務(wù)是通通過AOP動態(tài)代理實現(xiàn)的,這樣會產(chǎn)生一個代理類來做事務(wù)管理,而目標(biāo)類(service)本身是不能感知代理類的存在的。

對于加了@Transactional注解的方法來說,在調(diào)用代理類的方法時,會先通過攔截器TransactionInterceptor開啟事務(wù),然后在調(diào)用目標(biāo)類的方法,最后在調(diào)用結(jié)束后,TransactionInterceptor 會提交或回滾事務(wù),大致流程如下圖:

事務(wù)的調(diào)用原理

總結(jié),在方法 A 中調(diào)用方法 B,實際上是通過“this”的引用,也就是直接調(diào)用了目標(biāo)類的方法,而非通過 Spring 上下文獲得的代理類,所以事務(wù)是不會開啟的。

2. try...catch異常

在一段業(yè)務(wù)邏輯中對數(shù)據(jù)庫異常進行了處理,使用了try...catch子句捕獲異常并throw了一個自定義異常,這種情況導(dǎo)致了事務(wù)未回滾,示例代碼如下:

@Transactional(propagation = Propagation.REQUIRED, isolation = Isolation.READ_COMMITTED)
public boolean methodB(String name) throws BizException {
    try {
        rabbitDao.insertRabbit(name);
        tortoiseDao.insertTortoise(name);
    } catch (Exception e) {
        throw new BizException(ReturnCode.EXCEPTION.code, ReturnCode.EXCEPTION.msg);
    }
    return true;
}

BizException的定義如下:

public class BizException extends Exception {
    // 自定義異常
}

上面代碼中的聲明式事務(wù)在出現(xiàn)異常的時候,事務(wù)是不會回滾的。在代碼中我雖然捕獲了異常,但是同時我也拋出了異常,為什么事務(wù)未回滾呢?猜測是異常類型不對,于是開始查詢原因,翻看了Spring的官方文檔,找到了答案。下面是翻譯自Spring官網(wǎng)。

17.5.3 聲明式事務(wù)的回滾

上一節(jié)中介紹了如何設(shè)置開啟Spring事務(wù),一般在你的應(yīng)用的Service層代碼中設(shè)置,這一節(jié)將介紹在簡單流行的聲明式事務(wù)中如何控制事務(wù)回滾。

在Spring FrameWork 的事務(wù)框架中推薦的事務(wù)回滾方法是,在當(dāng)前執(zhí)行的事務(wù)上下文中拋出一個異常。如果異常未被處理,當(dāng)拋出異常調(diào)用堆棧的時候,Spring FrameWork 的事務(wù)框架代碼將捕獲任何未處理的異常,然后并決定是否將此事務(wù)標(biāo)記為回滾。

  • 在默認(rèn)配置中,Spring FrameWork 的事務(wù)框架代碼只會將出現(xiàn)runtime, unchecked 異常的事務(wù)標(biāo)記為回滾;也就是說事務(wù)中拋出的異常時RuntimeException或者是其子類,這樣事務(wù)才會回滾(默認(rèn)情況下Error也會導(dǎo)致事務(wù)回滾)。在默認(rèn)配置的情況下,所有的 checked 異常都不會引起事務(wù)回滾。

注:Unchecked Exception包括Error與RuntimeException. RuntimeException的所有子類也都屬于此類。另一類就是checked Exception。

  • 你可以精確的配置異常類型,指定此異常類事務(wù)回滾,包括 checked 異常。下面的xml代碼片段展示了如何配置checked異常引起事務(wù)回滾,應(yīng)用自定義異常類型:
<tx:advice id="txAdvice" transaction-manager="txManager">
    <tx:attributes>
    <tx:method name="get*" read-only="true" rollback-for="NoProductInStockException"/>
    <tx:method name="*"/>
    </tx:attributes>
</tx:advice>

與其有同等作用的注解形式如下:

@Transactional(rollbackForClassName={"NoProductInStockException"})
或者
@Transactional(rollbackFor={NoProductInStockException.class})
  • 在你遇到異常不想回滾事務(wù)的時候,同樣的你也可指定不回滾的規(guī)則,下面的一個例子告訴你,即使遇到未處理的 InstrumentNotFoundException 異常時,Spring FrameWork 的事務(wù)框架同樣會提交事務(wù),而不回滾。
<tx:advice id="txAdvice">
    <tx:attributes>
    <tx:method name="updateStock" no-rollback-for="InstrumentNotFoundException"/>
    <tx:method name="*"/>
    </tx:attributes>
</tx:advice>

與其有同樣作用的注解形式如下:

@Transactional(noRollbackForClassName={"InstrumentNotFoundException"})
或者
@Transactional(noRollbackFor={InstrumentNotFoundException.class})
  • 還有更靈活的回滾規(guī)則配置方法,同時指定什么異?;貪L,什么異常不回滾。當(dāng)Spring FrameWork 的事務(wù)框架捕獲到一個異常的時候,會去匹配配置的回滾規(guī)則來決定是否標(biāo)記回滾事務(wù),使用匹配度最強的規(guī)則結(jié)果。因此,下面的配置例子表達(dá)的意思是,除了異常 InstrumentNotFoundException 之外的任何異常都會導(dǎo)致事務(wù)回滾。
<tx:advice id="txAdvice">
    <tx:attributes>
    <tx:method name="*" rollback-for="Throwable" no-rollback-for="InstrumentNotFoundException"/>
    </tx:attributes>
</tx:advice>
  • 你也可以通過編程式的方式回滾一個事務(wù),盡管方法非常簡單,但是也有非常強的代碼侵入性,使你的業(yè)務(wù)代碼和Spring FrameWork 的事務(wù)框架代碼緊密的綁定在一起,示例代碼如下:
public void resolvePosition() {
    try {
        // some business logic...
    } catch (NoProductInStockException ex) {
        // trigger rollback programmatically
        TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
    }
}

如果可能的話,強烈推薦您使用聲明式事務(wù)方式回滾事務(wù),對于編程式事務(wù),如果你強烈需要它,也是可以使用的,but its usage flies in the face of achieving a clean POJO-based architecture.(沒懂...)

看完官方文檔這節(jié)內(nèi)容找到了問題的答案,原來是因為我們自定義的異常不是 RuntimeException。我的解決辦法是,在注解@Transactional中添加 rollbackFor={BizException.class}??赡苣銜栁覟槭裁床粚⒆远x異常修改為繼承RuntimeException,因為我需要BizException是一個checked 異常。

結(jié)束語:終于將spring事務(wù)中的異?;貪L機制搞明白啦,歡迎讀者在評論區(qū)添加其他導(dǎo)致spring事務(wù)不回滾的原因。

最后編輯于
?著作權(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)容

  • 這部分的參考文檔涉及數(shù)據(jù)訪問和數(shù)據(jù)訪問層和業(yè)務(wù)或服務(wù)層之間的交互。 Spring的綜合事務(wù)管理支持覆蓋很多細(xì)節(jié),然...
    竹天亮閱讀 1,094評論 0 0
  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理,服務(wù)發(fā)現(xiàn),斷路器,智...
    卡卡羅2017閱讀 136,506評論 19 139
  • 事務(wù)有四個特性:ACID 原子性(Atomicity):事務(wù)是一個原子操作,由一系列動作組成。事務(wù)的原子性確保動作...
    jiangmo閱讀 1,290評論 0 7
  • 一.聲明式事務(wù)實現(xiàn) 將編程式事務(wù)章節(jié)中applicationContext.xml修改下: 聲明式事務(wù)通過AOP代...
    zlb閱讀 1,041評論 0 1
  • 很多人喜歡這篇文章,特此同步過來 由淺入深談?wù)搒pring事務(wù) 前言 這篇其實也要歸納到《常識》系列中,但這重點又...
    碼農(nóng)戲碼閱讀 4,904評論 2 59

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