Spring Framework中常見的事務傳播陷阱(譯文)
最近看到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)
小結:
(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中常見的事務傳播陷阱(譯文)