Spring事務傳播

事務特性

事務有四大特性,分別以下:spring

1. 原子性(Atomicity):事務是數據庫邏輯工做單元,事務中包含的操做要麼都執行成功,要麼都執行失敗。數據庫

2. 一致性(Consistency):事務執行的結果必須是使數據庫數據從一個一致性狀態變到另一種一致性狀態。當事務執行成功後就說數據庫處於一致性狀態。若是在執行過程當中發生錯誤,這些未完成事務對數據庫所作的修改有一部分已寫入物理數據庫,這是數據庫就處於不一致狀態。併發

3. 隔離性(Isolation):一個事務的執行過程當中不能影響到其餘事務的執行,即一個事務內部的操做及使用的數據對其餘事務是隔離的,併發執行各個事務之間無不干擾。框架

4. 持續性(Durability):即一個事務執一旦提交,它對數據庫數據的改變是永久性的。以後的其它操做不該該對其執行結果有任何影響。函數

事務致使的問題

1. 髒讀:指當一個事務正字訪問數據,而且對數據進行了修改,而這種數據尚未提交到數據庫中,這時,另一個事務也訪問這個數據,而後使用了這個數據。由於這個數據尚未提交那麼另一個事務讀取到的這個數據咱們稱之爲髒數據測試

2. 不可重複讀:指在一個事務內,屢次讀同一數據。在這個事務尚未執行結束,另一個事務也訪問該同一數據,那麼在第一個事務中的兩次讀取數據之間,因爲第二個事務的修改第一個事務兩次讀到的數據多是不同的,這樣就發生了在一個事物內兩次連續讀到的數據是不同的,這種狀況被稱爲是不可重複讀。(針對行內數據字段不一致)this

3. 幻象:一個事務前後讀取一個範圍的記錄,但兩次讀取的紀錄數不一樣,咱們稱之爲幻象讀(兩次執行同一條 select 語句會出現不一樣的結果,第二次讀會增長一數據行)(針對查詢結果集條數不一致)spa

不可重複讀的和幻讀很容易混淆,不可重複讀側重於修改,幻讀側重於新增或刪除。解決不可重複讀的問題只需鎖住知足條件的行,解決幻讀須要鎖表代理

Spring中事務傳播屬性

Spring中事務很不少功能都是Spring藉助底層資源的功能來完成的,可是事務的傳播行爲是經過Spring框架自身實現的,Spring中定義了7個以PROPAGATION_開頭的常量表示它的傳播屬性,具體以下:code

1. PROPAGATION_REQUIRED:若是存在一個事務,則支持當前事務。若是沒有事務則開啓

2. PROPAGATION_SUPPORTS:若是存在一個事務,支持當前事務。若是沒有事務,則非事務的執行

3. PROPAGATION_MANDATORY:若是已經存在一個事務,支持當前事務。若是沒有一個活動的事務,則拋出異常

4. PROPAGATION_REQUIRES_NEW:老是開啓一個新的事務。若是一個事務已經存在,則將這個存在的事務掛起

5. PROPAGATION_NOT_SUPPORTED:老是非事務地執行,並掛起任何存在的事務

6. PROPAGATION_NEVER: 老是非事務地執行,若是存在一個活動事務,則拋出異常

7. PROPAGATION_NESTED:若是一個活動的事務存在,則運行在一個嵌套的事務中. 若是沒有活動事務, PROPAGATION_REQUIRED 屬性執行

Spring中事務的隔離級別

1. ISOLATION_DEFAULT:這是一個PlatfromTransactionManager默認的隔離級別,使用數據庫默認的事務隔離級別  

2. ISOLATION_READ_UNCOMMITTED :這是事務最低的隔離級別,它充許別外一個事務能夠看到這個事務未提交的數據。這種隔離級別會產生髒讀,不可重複讀和幻像讀。

3. ISOLATION_READ_COMMITTED :保證一個事務修改的數據提交後才能被另一個事務讀取。另一個事務不能讀取該事務未提交的數據。這種事務隔離級別能夠避免髒讀出現,可是可能會出現不可重複讀和幻像讀。:

4. ISOLATION_REPEATABLE_READ :這種事務隔離級別能夠防止髒讀,不可重複讀。可是可能出現幻像讀。

5. ISOLATION_SERIALIZABLE :這是花費最高代價可是最可靠的事務隔離級別。事務被處理爲順序執行。除了防止髒讀,不可重複讀外,還避免了幻讀。、

事務的隔離級別不一樣,有可能產生不一樣的錯誤現象,引用網上經常使用的一張圖說明隔離級別與各類問題的關係:

Spring中的事務超時

事務超時指的是一個事務所容許執行的最長時間,超過該時間限制後事務尚未完成,將自動回滾事務。Spring框架中在 TransactionDefinition接口中以 int 的值來表示超時時間,單位是秒。默認設置爲底層事務系統的超時值,若是底層數據庫事務系統沒有設置超時值,那麼就是none,沒有超時限制

Spring中事務的回滾規則

Spring事務管理器回滾一個事務的時機是在當前事務的上下文中拋出異常時。Spring事務管理器會捕捉到未處理的異常,而後根據規則決定是否須要回滾當前事務。

默認配置下,spring只有在拋出RuntimeException 異常(Errors也會致使事務回滾)時纔回滾,而拋出checked異常則不會致使事務回滾。可是咱們也能夠明確的配置在拋出哪些異常時回滾事務,包括checked異常(或者配置爲不回滾)。

Spring中事務傳播行爲

下面經過在Springboot中的幾個示例,看一下Spring中不一樣的事務傳播行爲。

PROPAGATION_REQUIRED

  1.A方法開啓事務,B方法中拋出異常

 1 @Service
 2 public class ProductServiceA {
 3 
 4     @Autowired
 5     private ProductDao productDao;
 6     
 7 @Autowired
 8 private ProductServiceB productServiceB;
 9     
10     @Transactional(propagation = Propagation.REQUIRED)
11     public void A() {
12         productDao.insert();
13         productServiceB.B();
14     }
15 }
16 
17 @Service
18 public class ProductServiceB {
19 
20     @Autowired
21     private ProductDao productDao;
22 
23     @Transactional(propagation = Propagation.REQUIRED)
24     public void B() {
25         productDao.update();
26         throw  new ArithmeticException("測試異常");
27     }
28 }

  測試結果:A和B都沒有執行成功

2.A方法開啓事務,A方法中拋出異常

 1 @Service
 2 public class ProductServiceA {
 3 
 4     @Autowired
 5     private ProductDao productDao;
 6 
 7 @Autowired
 8 private ProductServiceB productServiceB;
 9 
10     @Transactional(propagation = Propagation.REQUIRED)
11     public void A() {
12         productDao.insert();
13         productServiceB.B();
14 
15         throw  new ArithmeticException("測試異常");
16     }
17 }
18 
19 @Service
20 public class ProductServiceB {
21 
22     @Autowired
23     private ProductDao productDao;
24 
25     @Transactional(propagation = Propagation.REQUIRED)
26     public void B() {
27         productDao.update();
28     }
29 }

測試結果:A和B都沒有執行成功

3.A方法沒開啓事務,A方法中拋出異常

 1 @Service
 2 public class ProductServiceA {
 3 
 4     @Autowired
 5     private ProductDao productDao;
 6 
 7   @Autowired
 8   private ProductServiceB productServiceB;
 9 
10     public void A() {
11         productDao.insert();
12         productServiceB.B();
13         throw  new ArithmeticException("測試異常");
14     }
15 }
16 @Service
17 public class ProductServiceB {
18 
19     @Autowired
20     private ProductDao productDao;
21 
22     @Transactional(propagation = Propagation.NEVER)
23     public void B() {
24         productDao.update();
25     }
26 }

測試結果:A和B都執行成功

4.A方法沒開啓事務,B方法中拋出異常

 1 @Service
 2 public class ProductServiceA {
 3 
 4     @Autowired
 5     private ProductDao productDao;
 6     @Autowired
 7     private ProductServiceB productServiceB;
 8 
 9     //@Transactional(propagation = Propagation.REQUIRED)
10     public void A() {
11         productDao.insert();
12         productServiceB.B();
13     }
14 }
15 @Service
16 public class ProductServiceB {
17 
18     @Autowired
19     private ProductDao productDao;
20 
21     @Transactional(propagation = Propagation.REQUIRED)
22     public void B() {
23         productDao.update();
24         throw  new ArithmeticException("測試異常");
25     }
26 }

測試結果:A執行成功,B執行失敗

綜上,PROPAGATION_REQUIRED屬性:若是當執行到B方法時,會判斷上下文中是否存在事務,若是存在,則加入,此時方法A和方法B用的是同一個事務,方法A和方法B共存亡。若是不存在則方法B會爲本身分配一個事務,該事務與方法A無關,因此這種狀況可能出現A成功,B執行失敗回滾了。PROPAGATION_REQUIRED屬性是Spring框架事務傳播的默認屬性。

RROPAGATION_REQUIRES_NEW

1.A方法沒開啓事務,B方法中拋出異常

 1 @Service
 2 public class ProductServiceA {
 3 
 4     @Autowired
 5     private ProductDao productDao;
 6     @Autowired
 7     private ProductServiceB productServiceB;
 8 
 9     @Transactional(propagation = Propagation.REQUIRED)
10     public void A() {
11         productDao.insert();
12         productServiceB.B();
13     }
14 }
15 @Service
16 public class ProductServiceB {
17 
18     @Autowired
19     private ProductDao productDao;
20 
21     @Transactional(propagation = Propagation.REQUIRES_NEW)
22     public void B() {
23         productDao.update();
24         throw  new ArithmeticException("測試異常");
25     }
26 }

測試結果:A和B都執行失敗

2.A方法沒開啓事務,A方法自定義四回滾異常,B方法中拋出異常

 1 @Service
 2 public class ProductServiceA {
 3 
 4     @Autowired
 5     private ProductDao productDao;
 6     @Autowired
 7     private ProductServiceB productServiceB;
 8 
 9     //自定義回滾異常
10     @Transactional(propagation = Propagation.REQUIRED,noRollbackFor = ArithmeticException.class)
11     public void A() {
12         productDao.insert();
13         productServiceB.B();
14     }
15 }
16 @Service
17 public class ProductServiceB {
18 
19     @Autowired
20     private ProductDao productDao;
21 
22     @Transactional(propagation = Propagation.REQUIRES_NEW)
23     public void B() {
24         productDao.update();
25         throw  new ArithmeticException("測試異常");
26     }
27 }

測試結果:A方法執行成功,B方法執行失敗

3.A方法開啓事務,A方法拋異常

 1 @Service
 2 public class ProductServiceA {
 3 
 4     @Autowired
 5     private ProductDao productDao;
 6     @Autowired
 7     private ProductServiceB productServiceB;
 8 
 9     //自定義回滾異常
10     @Transactional(propagation = Propagation.REQUIRED)
11     public void A() {
12         productDao.insert();
13         productServiceB.B();
14         throw  new ArithmeticException("測試異常");
15     }
16 }
17 @Service
18 public class ProductServiceB {
19 
20     @Autowired
21     private ProductDao productDao;
22 
23     @Transactional(propagation = Propagation.REQUIRES_NEW)
24     public void B() {
25         productDao.update();
26 
27     }
28 }

測試結果:A方法執行失敗,B方法執行成功

4.A方法開啓事務,A方法拋異常

  同PROPAGATION_REQUIRED中示例,B方法單獨開啓一個事務

5.A方法開啓事務,B方法拋異常

  同PROPAGATION_REQUIRED中示例,B方法單獨開啓一個事務

綜上,RROPAGATION_REQUIRES_NEW屬性老是開啓一個新的事務,若是已經存在一個事務,則將這個事務掛起。PROPAGATION_REQUIRED 和RROPAGATION_REQUIRES_NEW的事務傳遞的區別在於後者老是新起一個事務,因此可能存在兩個獨立的事務。

PROPAGATION_SUPPORTS

當方法B加上@Transactional(propagation = Propagation.SUPPORTS)註解,執行到方法B時,會檢查上下文中是否是已經存在事務,存在則加入,此時至關於PROPAGATION_REQUIRED,不存在則以非事務方法運行,此時恰好和RROPAGATION_REQUIRES_NEW相反。這種方式總結起來就是有事務就加入,沒有就算。

PROPAGATION_NOT_SUPPORTED

當方法B加上@Transactional(propagation = Propagation.NOT_SUPPORTED)註解,執行方法B時,檢查上下文中有沒有事務,若是沒有則正常以非事務方式執行,若是有事務,則掛起當前事務,繼續以非事務方式執行完,而後在繼續執行當前事務。這種方式總結起來就是有事務也不支持。方法B必定以非事務方式運行,不存在回滾方法B的可能。

PROPAGATION_NESTED

1.A方法拋出異常

 1 @Service
 2 public class ProductServiceA {
 3 
 4     @Autowired
 5     private ProductDao productDao;
 6     @Autowired
 7     private ProductServiceB productServiceB;
 8 
 9     //自定義回滾異常
10     @Transactional(propagation = Propagation.REQUIRED)
11     public void A() {
12         productDao.insert();
13         productServiceB.B();
14         throw  new ArithmeticException("測試異常");
15     }
16 }
17 @Service
18 public class ProductServiceB {
19 
20     @Autowired
21     private ProductDao productDao;
22 
23     @Transactional(propagation = Propagation.NESTED)
24     public void B() {
25         productDao.update();
26 
27     }
28 }

測試結果:A和B都執行失敗。

2.B方法拋出異常,主事務可選擇性處理該異常

 1 @Service
 2 public class ProductServiceA {
 3 
 4     @Autowired
 5     private ProductDao productDao;
 6     @Autowired
 7     private ProductServiceB productServiceB;
 8 
 9     //自定義回滾異常
10     @Transactional(propagation = Propagation.REQUIRED,noRollbackFor = ArithmeticException.class)
11     //@Transactional(propagation = Propagation.REQUIRED)
12     public void A() {
13         productDao.insert();
14         productServiceB.B();
15 
16     }
17 }
18 @Service
19 public class ProductServiceB {
20 
21     @Autowired
22     private ProductDao productDao;
23 
24     @Transactional(propagation = Propagation.NESTED)
25     public void B() {
26         productDao.update();
27         throw  new ArithmeticException("測試異常");
28     }
29 }

當方法B加上@Transactional(propagation = Propagation.NESTED)註解,執行方法B時會先判斷上下文中是否是存在事務,若是不存在則,至關於RROPAGATION_REQUIRES_NEW屬性,新起一個事務運行,若是存在,則在當前事務中嵌套一個事務,此時也出現兩個事務,是嵌套關係。若是主事務提交或者回滾,則嵌套事務也將提交或者回滾。嵌套事務異常,主事務可選擇回滾仍是繼續執行,主要是看如何處理這個異常。

PROPAGATION_NESTED與RROPAGATION_REQUIRES_NEW的區別是PROPAGATION_NESTED建立的是外部事務的子事務, 若是外部事務commit/roolback, 嵌套事務也會被commit/rollback,而RROPAGATION_REQUIRES_NEW建立的是一個新的事務,與已存在的事務獨立,即方法A和方法B是分別獨立的兩個事務,方法B若是已經執行,此時方法A拋異常將不影響方法B。

PROPAGATION_NEVER

這種方式代表必須以非事務的方式運行,若是存在事務,則拋異常。

PROPAGATION_MANDATORY

這個和PROPAGATION_NEVER相反,必須以事務的方式運行,不存在則拋異常。

事務的傳播行爲失效問題

若是在程序中出如今同一個類的內部出現調用另外一個@Transactional註解函數,或者在private或者protected方法上使用@Transactional註解,則將可能出現傳播行爲不起做用。

緣由:因爲Spring的事務是經過AOP來實現的,Spring中經過動態代理實現Aop功能,不論是jdk動態代理仍是cglib動態代理,都不會處理目標類的private和protected方法。而若是在類的內部調用另一個添加了@Transactional註解的函數,至關於經過this調用函數,而不是代理對象調用該函數,因此Spring也不會處理這個傳播行爲。

相關文章
相關標籤/搜索