1. 程式人生 > >Spring Framework中常見的事務傳播陷阱(譯文)

Spring Framework中常見的事務傳播陷阱(譯文)

及其 framework 進行 能開 bubuko 不起作用 互相調用 spring req

最近看到Medium上一篇討論Spring Framework中事務傳播的文章,解釋了幾種常見的問題,解釋的不錯,這裏直接翻譯吧(意譯為主,粗體和斜體是我自己加上的)。

譯文:

這是我的第一篇文章,我打算給大家總結一下開發者在使用Spring事務時,常常會犯的和事務傳播相關的錯誤。

在這之前,我們先回憶一下Spring中事務是怎樣傳播的。

我們先看下Ken Tousen做的有關事務傳播註解的筆記(譯註:這裏指@Transactional註解的propagation屬性),這會幫助我們更清楚地記憶。

技術分享圖片

1. 私有方法

當公有方法調用私有方法時,不會創建新事務。(譯註:這裏有點問題,見下面評論)

 1 @Transactional
 2 public class MusicPlayerService {
 3     
 4     @Autowired
 5     Song song;
 6 
 7     @Autowired
 8     MusicCatalogueService musicCatalogueService;
 9 
10     public void playSong() {
11         scrobble();
12         song.play();
13     }
14 
15     @Transactional(propagation = Propagation.REQUIRES_NEW)
16 private void scrobble() { 17 musicCatalogueService.scrobble(song); 18 } 19 }

上面這個例子裏,如果musicCatalogueService.scrobble(song)調用失敗了,playSong()也會失敗。

因為playSong()scrobble()在同一個事務中。

所以要開啟新事務,方法必須聲明為public。

譯註:

這裏有點問題。即使將scrobble()聲明為public,scrobble()也不會開啟新事務。

這是因為Spring是使用AOP來進行事務管理的。而scrobble()和playSong()在一個類裏,當playSong()調用scrobble()時,是直接調用scrobble()而不是調用代理方法,所以這兩個方法都在同一個事務中。而musicCatalogueService.scrobble(song)是另一個對象(musicCatalogueService)

中的方法,這個對象由Spring註入,是可以通過AOP代理的,也就可以通過註解開啟新事務(當然前提是,musicCatalogueService的方法有註解)。

小結:

(1)@Transactional註解對私有方法不起作用,只對公有方法生效。

(2)Spring使用AOP進行事務管理,因此同一個類中的方法互相調用,在不涉及其他類的方法時,如果第一個方法開啟了事務,那麽接下來所有的方法都處於同一個事務中(不論其他方法是否私有/公有、是否有註解、註解的傳播級別是什麽)。

(3)只有調用不同類的方法時,才有可能開啟新事務。

2. 同一個類中的方法調用

如果一個方法調用另一個方法,而這個被調用方法要開啟新事務的話,它必須在另一個類中。

不然,這兩個方法在同一個事務中。

把方法放在不同的類中來開啟新事務的例子:

 1 @Transactional
 2 public class MusicPlayerService {
 3 
 4     @Autowired
 5     Song song;
 6 
 7     @Autowired
 8     MusicCatalogueService musicCatalogueService;
 9 
10     public void playSong() {
11         musicCatalogueService.scrobble(song);
12         song.play();
13     }
14 }
15 
16 public class MusicCatalogueService {
17 
18     @Autowired
19     ScrobbleRepository scrobbleRepository;
20 
21     @Transactional(propagation = Propagation.REQUIRES_NEW)
22     public void scrobble(Song song) {
23         scrobbleRepository.save(song);
24     }
25 }

3. SUPPORTS和readonly結合

如果沒有正在進行的事務,那麽SUPPORTS不會創建新事務。readonly會被忽視。

4. CRD操作和readonly結合

如果我們使用了REQUIRED或者REQUIRES_NEW,以及readonly,然後嘗試做CREATE/DELETE/UPDATE操作,那麽程序會拋出只讀異常。

5. Checked Exceptions

如果程序拋出RuntimeException,事務會回滾。這很合理。

但是如果程序拋出的是checked exceptions呢?

答案是:事務不會回滾。

那怎樣去處理這種情況?

Spring提供了rollbackFor屬性。通過指定要回滾的異常類型,可以確保在程序拋出checked exceptions時事務回滾。用法如下:

@Transactional(rollbackFor = Exception.class)
public void rollBackActionDefinedMethod() {
}

譯文完。

Spring Framework中常見的事務傳播陷阱(譯文)