前言
Spring在TransactionDefinition介面中規定了7種型別的事務傳播行為。事務傳播行為是Spring框架獨有的事務增強特性,他不屬於的事務實際提供方資料庫行為。這是Spring為我們提供的強大的工具箱,使用事務傳播行可以為我們的開發工作提供許多便利。但是人們對他的誤解也頗多,你一定也聽過“service方法事務最好不要巢狀”的傳言。要想正確的使用工具首先需要了解工具。
基礎概念
1. 什麼是事務傳播行為?
事務傳播行為用來描述由某一個事務傳播行為修飾的方法被巢狀進另一個方法的時事務如何傳播。
用虛擬碼說明:
1 @Transaction(Propagation=XXX) 2 public void methodA(){ 3 methodB(); 4 //doSomething 5 } 6 7 public void methodB(){ 8 //doSomething 9 }
methodA中存在事務,他又呼叫了methodB。methodB事物的一些特性由methodA決定,這就是事務的傳播行為。
2. Spring中七種事務傳播行為
事務傳播行為型別 | 說明 |
---|---|
PROPAGATION_REQUIRED | 如果當前沒有事務,就新建一個事務,如果已經存在一個事務中,加入到這個事務中。這是最常見的選擇。 |
PROPAGATION_SUPPORTS | 支援當前事務,如果當前沒有事務,就以非事務方式執行。 |
PROPAGATION_MANDATORY | 使用當前的事務,如果當前沒有事務,就丟擲異常。 |
PROPAGATION_REQUIRES_NEW | 新建事務,如果當前存在事務,把當前事務掛起。 |
PROPAGATION_NOT_SUPPORTED | 以非事務方式執行操作,如果當前存在事務,就把當前事務掛起。 |
PROPAGATION_NEVER | 以非事務方式執行,如果當前存在事務,則丟擲異常。 |
PROPAGATION_NESTED | 如果當前存在事務,則在巢狀事務內執行。如果當前沒有事務,則執行與PROPAGATION_REQUIRED類似的操作。 |
定義非常簡單,也很好理解,下面我們就進入程式碼測試部分,驗證我們的理解是否正確。
程式碼驗證
第一種情況。內部均為 propagation = Propagation.REQUIRED
1 @Transactional(propagation = Propagation.REQUIRED, isolation = Isolation.REPEATABLE_READ) 2 @Override 3 public void testTransactional() { 4 int insert = downloadImgDao.test1(); 5 log.info("insert1 = {}", insert); 6 7 insert = downloadImgDao.test2(); 8 log.info("insert2 = {}", insert); 9 } 10 11 @Transactional(propagation = Propagation.REQUIRED, isolation = Isolation.REPEATABLE_READ) 12 @Override 13 public int test1() { 14 DownloadImg downloadImg = new DownloadImg(); 15 downloadImg.setId(666L); 16 downloadImg.setLink("張三"); 17 downloadImg.setLinkname("16"); 18 int res = downloadImgMapper.insertSelective(downloadImg); 19 log.info("res1 = {}", res); 20 return res; 21 } 22 23 24 @Transactional(propagation = Propagation.REQUIRED, isolation = Isolation.REPEATABLE_READ) 25 @Override 26 public int test2() { 27 DownloadImg downloadImg = new DownloadImg(); 28 downloadImg.setId(888L); 29 downloadImg.setLink("李四"); 30 downloadImg.setLinkname("18"); 31 int res = downloadImgMapper.insertSelective(downloadImg); 32 log.info("res2 = {}", res); 33 34 int i = 5 / 0; 35 return res; 36 }
張三,李四插入均失敗。
第二種情況。內部一種為 propagation = Propagation.REQUIRED,一種為Propagation.REQUIRES_NEW
1 @Transactional(propagation = Propagation.REQUIRES_NEW, isolation = Isolation.REPEATABLE_READ) 2 @Override 3 public int test1() { 4 DownloadImg downloadImg = new DownloadImg(); 5 downloadImg.setId(666L); 6 downloadImg.setLink("張三"); 7 downloadImg.setLinkname("16"); 8 int res = downloadImgMapper.insertSelective(downloadImg); 9 log.info("res1 = {}", res); 10 return res; 11 } 12 13 14 @Transactional(propagation = Propagation.REQUIRED, isolation = Isolation.REPEATABLE_READ) 15 @Override 16 public int test2() { 17 DownloadImg downloadImg = new DownloadImg(); 18 downloadImg.setId(888L); 19 downloadImg.setLink("李四"); 20 downloadImg.setLinkname("18"); 21 int res = downloadImgMapper.insertSelective(downloadImg); 22 log.info("res2 = {}", res); 23 24 int i = 5 / 0; 25 return res; 26 }
張三插入成功,李四插入失敗。
第三種情況。內部均為Propagation.REQUIRES_NEW
跟我們現象的是一樣的,如果哪個test異常,哪個就失敗,無異常的就成功。
還有一種情況是,外圍拋異常了,內部都不拋異常,兩種內部插入也都會成功。
結論
本程式是實驗了PROPAGATION_REQUIRED以及Propagation.REQUIRES_NEW。
事務預設以PROPAGATION_REQUIRED來隔離。
1: 如果內部是PROPAGATION_REQUIRED隔離級別,內部只要一個方法出錯,那麼整個事務都會回滾。
2: 如果內部有方法以Propagation.REQUIRES_NEW來隔離。那麼他會建立一個新的事務來執行,如果他拋異常了,並不會影響其他事務的以及外部的事務。
spring事務官方文件:https://docs.spring.io/spring/docs/5.2.6.RELEASE/spring-framework-reference/data-access.html#tx-propagation
參考文件:https://segmentfault.com/a/1190000013341344