1. 程式人生 > 其它 >Spring 雙層事務,我丟擲的異常去哪了?

Spring 雙層事務,我丟擲的異常去哪了?

技術標籤:程式語言bugjavazookeeperspring

作者:AMOS0626
來源:my.oschina.net/AmosWang/blog/4773386

系統 A 呼叫系統 B 執行資料同步,系統 B 返回了錯誤提示,系統 A 需要將前邊儲存的回滾掉,同時把錯誤資訊向上拋。

大致程式碼如下

@Service("noteService")
publicclassNoteServiceImplimplementsNoteService{

@Resource
privateSearchServicesearchService;


@Transactional(rollbackFor=Throwable.class)
@Override
publicCommonResponse<NoteEntity>save(NoteEntitynote){
//一系列DB操作

try{
searchService.sync(note);
}catch(Exceptione){
e.printStackTrace();
}

returnCommonResponse.success(entity);
}

}

@Service("searchService")
publicclassSearchServiceImplimplementsSearchService{

@Transactional(rollbackFor=Throwable.class)
@Override
publicvoidsync(NoteEntitynote){
//一系列DB操作

thrownewRuntimeException("同步異常![XXX]");
}

}

@SpringBootTest
publicclassNoteTests{

@Resource
privateNoteServicenoteService;

@Test
publicvoidsaveNote(){
NoteEntityentity=newNoteEntity();
entity.setTitle("念奴嬌赤壁懷古");
entity.setContent("大江東去,浪淘盡,千古風流人物。故壘西邊,人道是:三國周郎赤壁。。。");
entity.setTags("蘇軾,宋代");
entity.setCategory("蘇軾詩詞");

try{
noteService.save(entity);
}catch(Exceptione){
e.printStackTrace();
//FIXME我想在這裡拿到的是同步異常![XXX]
//FIXME但是這裡拿到的是Transactionsilentlyrolledbackbecauseithasbeenmarkedasrollback-only
System.out.println(">>>>>>>>>>"+e.getMessage());
}
}

}

事出有因

程式碼歷史久遠,為何這樣寫已無從追溯。

納悶了一會兒,看到雙層事務,就想起了 Spring事務傳播機制,前邊理解得比較膚淺。Spring 系列面試題和答案我全部整理好了,請關注公眾號網際網路架構師,回覆:2T。

沒有特殊的配置,自然是走預設的事務傳播機制了,也就是 Propagation.REQUIRED。

國際慣例,列出事務傳播機制:

1、PROPAGATION_REQUIRED
當前沒事務,則建立事務;存在事務,就加入該事務,這是最常用的設定。

2、PROPAGATION_SUPPORTS
當前存在事務,就加入事務,當前不存在事務,就以非事務方式執行。

3、PROPAGATION_MANDATORY
當前存在事務,就加入事務;當前不存在事務,就丟擲異常。

4、PROPAGATION_REQUIRES_NEW
無條件建立新事務。

5、PROPAGATION_NOT_SUPPORTED
以非事務方式執行,如果當前存在事務,就將當前事務掛起。

6、PROPAGATION_NEVER
以非事務方式執行,如果存在事務,就丟擲異常。

7、PROPAGATION_NESTED
開始執行事務前,先儲存一個savepoint,當發生異常時,就回滾到savepoint;沒有異常時,跟著外部事務一起提交或回滾。

具體原因

1、看了上邊的事務傳播機制,繼續細化問題,內外層共用一個事務,內層丟擲異常,會導致整個事務失敗。

2、繼續分析,外層邏輯進行了try catch,就導致內層的異常無法繼續向上丟擲,外層事務會繼續提交。

3、事務提交時,進行事務狀態的判斷,就發現這個事務是失敗的,需要回滾,所以丟擲了Transaction silently rolled back because it has been marked as rollback-only的異常。

另外,大家想學 Spring Boot 的看下這個倉庫,太全了。

https://github.com/javastacks/spring-boot-best-practice

怎麼解決?

銀彈自然是沒有的,根據業務場景選擇合適的方案。

1、當前這種場景,直接把外層邏輯中的 try catch 去掉即可。異常直接向上拋,事務就不會繼續提交,呼叫方拿到的就是一手的異常;

2、如果內層不是核心邏輯,記錄個日誌啥的,可以把內層事務配置為@Transactional(rollbackFor = Throwable.class, propagation = Propagation.REQUIRES_NEW), 無論如何,都建立新的事務,外層事務不受內層事務影響。但是有個問題,外層事務失敗了,內層事務還是把記錄入庫了,有可能產生髒資料;

3、如果外層事務失敗了,內層事務也不能提交,那就可以使用@Transactional(rollbackFor = Throwable.class, propagation = Propagation.NESTED)。注意:hibernate/jpa 不支援巢狀事務 NESTED,可用 JdbcTemplate 代替。

PS:如果覺得我的分享不錯,歡迎大家隨手點贊、在看。

大家一起在評論區聊聊唄~

關注微信公眾號:網際網路架構師,在後臺回覆:2T,可以獲取我整理的教程,都是乾貨。


猜你喜歡

1、GitHub 標星 3.2w!史上最全技術人員面試手冊!FackBoo發起和總結

2、如何才能成為優秀的架構師?

3、從零開始搭建創業公司後臺技術棧

4、程式設計師一般可以從什麼平臺接私活?

5、37歲程式設計師被裁,120天沒找到工作,無奈去小公司,結果懵了...

6、滴滴業務中臺構建實踐,首次曝光

7、不認命,從10年流水線工人,到谷歌上班的程式媛,一位湖南妹子的勵志故事

8、15張圖看懂瞎忙和高效的區別

9、2T架構師學習資料乾貨分享