Propagation.NESTED 某些情況下會失效?

當 Propagation.NESTED 遇到 TransactionTimedOutException 時,會發現不僅內層嵌套事務回滾,外層事務也被一起回滾了。這是爲什麼?


首先明確幾個概念:

  1. TransactionTimedOutException 什麼時候會拋出來?答:事務中執行 SQL 前,會檢查當前事務是否超時,超時則會拋出該異常;
  2. Propagation.NESTED 傳播級別的使用場景?答:外層事務已經寫了一部分數據,但是有一部分數據並不關心是否執行成功,則可以給這部分邏輯的事務傳播級別設置爲 NESTED;


當拋出 TransactionTimedOutException 時,當前事務持有的 DataSource 會被標記爲 rollbackOnly(藉助 ResourceHolderSupport 實現),當整個大的事務提交時,會檢查 rollbackOnly 標記,若爲 true,即使內部並沒有對外拋出異常,事務也會被回滾。


那如何避免 rollbackOnly 回滾整個事務呢?重寫 DataSourceTransactionManager 的 shouldCommitOnGlobalRollbackOnly 方法即可:

    @Bean
    @Primary
    public DataSourceTransactionManager transactionManager(@Autowired DataSource dataSource) {
        return new DataSourceTransactionManager(dataSource) {
            @Override
            protected boolean shouldCommitOnGlobalRollbackOnly() {
                return true;
            }
        };
    }

但是這樣做是有風險的,如下面這段代碼:

class serviceA {
    @Autowired ServiceB serviceB;
    
    @Transaction
 	void methodA() {
        serviceB.methodB();
    }
}

class serviceB {
    @Transaction
 	void methodB() {
    	someDao.insert("inserted data");
        throw new RuntimeException("maked runtime exception");
    }
}

methodA() 執行後,正常情況 “inserted data” 是不應該被插入數據庫的,但是如果 shouldCommitOnGlobalRollbackOnly 被重寫爲返回 true,"insert data"將被插入。其實 rollbackOnly 屬性就是爲了確保這種傳播級別爲 Propagation.REQUIRED 場景服務的。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章