在開發中,相信你們都使用過Spring的事務管理功能。那麼,你是否有了解過,Spring的事務傳播行爲呢?java
Spring中,有7種類型的事務傳播行爲。事務傳播行爲是Spring框架提供的一種事務管理方式,它不是數據庫提供的。不知道你們是否據說過「不要在service事務方法中嵌套事務方法,這樣會提交多個事務」的說法,其實這是不許確的。瞭解了事務傳播行爲以後,相信你就會明白!mysql
本文首發於頭條號【Happyjava】。Happy的掘金地址:https://juejin.im/user/5cc2895df265da03a630ddca,Happy的我的博客:(http://blog.happyjava.cn)[http://blog.happyjava.cn]。歡迎轉載,但須保留此段聲明。spring
事務的傳播行爲,默認值爲 Propagation.REQUIRED。能夠手動指定其餘的事務傳播行爲,以下:sql
若是當前存在事務,則加入該事務,若是當前不存在事務,則建立一個新的事務。數據庫
若是當前存在事務,則加入該事務;若是當前不存在事務,則以非事務的方式繼續運行。app
若是當前存在事務,則加入該事務;若是當前不存在事務,則拋出異常。框架
從新建立一個新的事務,若是當前存在事務,延緩當前的事務。ide
以非事務的方式運行,若是當前存在事務,暫停當前的事務。測試
以非事務的方式運行,若是當前存在事務,則拋出異常。spa
若是沒有,就新建一個事務;若是有,就在當前事務中嵌套其餘事務。
數據庫表:
CREATE TABLE `t_user` ( `id` int(11) NOT NULL, `password` varchar(255) DEFAULT NULL, `username` varchar(255) DEFAULT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
一個整合了Spring Data JPA的SpringBoot工程,這裏就很少說了。
默認的事務傳播行爲是Propagation.REQUIRED,也就是說:若是當前存在事務,則加入該事務,若是當前不存在事務,則建立一個新的事務。
下面,咱們就驗證下前面說的「不要循環嵌套事務方法」的問題:
如今有兩個Service,以下:
@Service public class UserService { @Autowired private UserRepo userRepo; @Transactional(propagation = Propagation.REQUIRED) public void insert() { UserEntity user = new UserEntity(); user.setUsername("happyjava"); user.setPassword("123456"); userRepo.save(user); } }
這裏很簡單,就一個insert插入用戶的方法。
@Service public class UserService2 { @Autowired private UserService userService; @Transactional public void inserBatch() { for (int i = 0; i < 10; i++) { if (i == 9) { throw new RuntimeException(); } userService.insert(); } } }
注入UserService,循環十次調用參數方法。而且第十次拋出異常。調用inserBatch方法,查看結果:
@Test public void insertBatchTest() { userService2.inserBatch(); }
結果以下:
數據庫中沒有記錄:
這也證實了「若是當前存在事務,則加入該事務」的概念。若是之後還碰到有人說不要循環嵌套事務的話,能夠叫他回去好好看看Spring的事務傳播行爲。
若是當前存在事務,則加入該事務;若是當前不存在事務,則以非事務的方式繼續運行。也就是說,該模式是否支持事務,看調用它的方法是否有事務支持。測試代碼以下:
@Transactional(propagation = Propagation.SUPPORTS) public void insert() { UserEntity user = new UserEntity(); user.setUsername("happyjava"); user.setPassword("123456"); userRepo.save(user); throw new RuntimeException(); }
public void insertWithoutTx() { userService.insert(); }
調用的方法沒有開啓事務,運行結果:
運行報錯了,可是數據卻沒有回滾掉。說明了insert方法是沒有在事務中運行的。
若是當前存在事務,則加入該事務;若是當前不存在事務,則拋出異常。mandatory中文是強制性的意思,代表了被修飾的方法,必定要在事務中去調用,不然會拋出異常。
@Transactional(propagation = Propagation.MANDATORY) public void insert() { UserEntity user = new UserEntity(); user.setUsername("happyjava"); user.setPassword("123456"); userRepo.save(user); }
UserService2.java
public void insertWithoutTx() { userService.insert(); }
調用:
@Test public void insertWithoutTxTest() { userService2.insertWithoutTx(); }
運行結果:
拋出了異常,提示沒有存在的事務。
這個理解起來可能會比較繞,官方的解釋是這樣子的:
Create a new transaction, and suspend the current transaction if one exists.
大意就是:從新建立一個新的事務,若是當前存在事務,延緩當前的事務。這個延緩,或者說掛起,可能理解起來比較難,下面經過例子來分析:
@Transactional(propagation = Propagation.REQUIRES_NEW) public void insert() { UserEntity user = new UserEntity(); user.setUsername("happyjava"); user.setPassword("123456"); userRepo.save(user); }
這個insert方法的傳播行爲改成REQUIRES_NEW。
@Transactional public void inserBatch() { UserEntity user = new UserEntity(); user.setUsername("初次調用"); user.setPassword("123456"); userRepo.save(user); for (int i = 0; i < 10; i++) { if (i == 9) { throw new RuntimeException(); } userService.insert(); } }
inserBatch擁有事務,而後後面循環調用的insert方法也有本身的事務。根據定義,inserBatch的事務會被延緩。具體表現就是:後面的10次循環的事務在每次循環結束以後都會提交本身的事務,而inserBatch的事務,要等循環方法走完以後再提交。但因爲第10次循環會拋出異常,則inserBatch的事務會回滾,既數據庫中不會存在:「初次調用」的記錄:
測試代碼:
@Test public void insertBatchTest() { userService2.inserBatch(); }
執行結果:
這種狀況,符合開始說的「不要循環嵌套事務方法」的說話,固然是否須要循環嵌套,仍是要看業務邏輯的。
Execute non-transactionally, suspend the current transaction if one exists.
以非事務的方式運行,若是當前存在事務,暫停當前的事務。這種方式與REQUIRES_NEW有所相似,可是NOT_SUPPORTED修飾的方法其自己是沒有事務的。這裏就不作代碼演示了。
以非事務的方式運行,若是當前存在事務,則拋出異常。
@Transactional(propagation = Propagation.NEVER) public void insert() { UserEntity user = new UserEntity(); user.setUsername("happyjava"); user.setPassword("123456"); userRepo.save(user); }
@Transactional public void insertWithTx() { userService.insert(); }
執行結果:
若是沒有事務,就新建一個事務;若是有,就在當前事務中嵌套其餘事務。
這個也是理解起來比較費勁的一個行爲。咱們一步一步分析。
外圍方法沒有事務:這種狀況跟REQUIRED是同樣的,會新建一個事務。
外圍方法若是存在事務:這種狀況就會嵌套事務。所謂嵌套事務,大意就是,外圍事務回滾,內嵌事務必定回滾,而內嵌事務能夠單獨回滾而不影響外圍主事務和其餘子事務。
因爲本人使用Spring Data JPA 進行的演示代碼,使用嵌套事務會提示:
org.springframework.transaction.NestedTransactionNotSupportedException: JpaDialect does not support savepoints - check your JPA provider's capabilities
搜索了下,hibernate彷佛不支持這種事務傳播方式。因此這裏就不作演示了
事務傳播行爲,在開發中可能不會特別的留意到它(更多時候,咱們可能只是使用默認的方式),可是仍是須要對其要有所理解。但願本篇文章能讓你們明白Spring的7種事務傳播行爲。