什么是事務?
事務邏輯上的一組操作,組成這組操作的各個邏輯單元,要么一起成功,要么一起失敗.
事務特性(4種):
原子性 (atomicity):強調事務的不可分割.
一致性 (consistency):事務的執行的前后數據的完整性保持一致.
隔離性 (isolation):一個事務執行的過程中,不應該受到其他事務的干擾
持久性(durability) :事務一旦結束,數據就持久到數據庫
如果不考慮隔離性引發安全性問題:
臟讀 :一個事務讀到了另一個事務的未提交的數據
不可重復讀 :一個事務讀到了另一個事務已經提交的 update 的數據導致多次查詢結果不一致. (針對某條記錄,兩次查詢不一致)
虛幻讀 :一個事務讀到了另一個事務已經提交的 insert 的數據導致多次查詢結果不一致.(針對某張表,兩次查詢到條數不一致,查到了多的數據)
事務隔離級別(5種)
DEFAULT 這是一個PlatfromTransactionManager默認的隔離級別,使用數據庫默認的事務隔離級別.
未提交讀(read uncommited) :臟讀,不可重復讀,虛讀都有可能發生
已提交讀 (read commited):避免臟讀。但是不可重復讀和虛讀有可能發生
可重復讀 (repeatable read) :避免臟讀和不可重復讀.但是虛讀有可能發生.
串行化的 (serializable) :避免以上所有讀問題.
read uncommited:是最低的事務隔離級別,它允許另外一個事務可以看到這個事務未提交的數據。
read commited:保證一個事物提交后才能被另外一個事務讀取。另外一個事務不能讀取該事物未提交的數據。
repeatable read:這種事務隔離級別可以防止臟讀,不可重復讀。但是可能會出現幻象讀。它除了保證一個事務不能被另外一個事務讀取未提交的數據之外還避免了以下情況產生(不可重復讀)。
serializable:這是花費最高代價但最可靠的事務隔離級別。事務被處理為順序執行。除了防止臟讀,不可重復讀之外,還避免了幻象讀(避免三種)。
Mysql 默認:可重復讀
Oracle 默認:讀已提交
什么是事務的傳播行為?
即然是傳播,那么至少有兩個東西,才可以發生傳播。單體不存在傳播這個行為。
事務傳播行為(propagation behavior)指的就是當一個事務方法被另一個事務方法調用時,這個事務方法應該如何進行。
例如:methodA事務方法調用methodB事務方法時,methodB是繼續在調用者methodA的事務中運行呢,還是為自己開啟一個新事務運行,這就是由methodB的事務傳播行為決定的。
在Spring種定義有7種傳播行為:
保證同一個事務中(三種)
1、PROPAGATION_REQUIRED 支持當前事務,如果不存在 就新建一個(默認)
1 @Transactional(propagation = Propagation.REQUIRED) 2 public void methodA() { 3 methodB(); 4 // do something
5 } 6
7 @Transactional(propagation = Propagation.REQUIRED) 8 public void methodB() { 9 // do something
10 }
單獨調用methodB方法時,因為當前上下文不存在事務,所以會開啟一個新的事務。 調用methodA方法時,因為當前上下文不存在事務,所以會開啟一個新的事務。當執行到methodB時,methodB發現當前上下文有事務,因此就加入到當前事務中來。
2、PROPAGATION_SUPPORTS 支持當前事務,如果不存在,就不使用事務
1 @Transactional(propagation = Propagation.REQUIRED) 2 public void methodA() { 3 methodB(); 4 // do something
5 } 6
7 // 事務屬性為SUPPORTS
8 @Transactional(propagation = Propagation.SUPPORTS) 9 public void methodB() { 10 // do something
11 }
單純的調用methodB時,methodB方法是非事務的執行的。當調用methdA時,methodB則加入了methodA的事務中,事務地執行。
3、PROPAGATION_MANDATORY 支持當前事務,如果不存在,拋出異常(mandatory:強制的)
1 @Transactional(propagation = Propagation.REQUIRED) 2 public void methodA() { 3 methodB(); 4 // do something
5 } 6
7 // 事務屬性為MANDATORY
8 @Transactional(propagation = Propagation.MANDATORY) 9 public void methodB() { 10 // do something
11 }
當單獨調用methodB時,因為當前沒有一個活動的事務,則會拋出異常throw new IllegalTransactionStateException(“Transaction propagation ‘mandatory’ but no existing transaction found”);當調用methodA時,methodB則加入到methodA的事務中,事務地執行。
保證沒有在同一個事務中(四種)
4、PROPAGATION_REQUIRES_NEW 如果有事務存在,掛起當前事務,創建一個新的事務
1 @Transactional(propagation = Propagation.REQUIRED) 2 public void methodA() { 3 doSomeThingA(); 4 methodB(); 5 doSomeThingB(); 6 // do something else
7 } 8
9 @Transactional(propagation = Propagation.REQUIRES_NEW) 10 public void methodB() { 11 // do something
12 }
當執行methodA()時,相當於執行了
1 TransactionManager tm = null; 2 try{ 3 //獲得一個JTA事務管理器
4 tm = getTransactionManager(); 5 tm.begin();//開啟一個新的事務
6 Transaction ts1 = tm.getTransaction(); 7 doSomeThing(); 8 tm.suspend();//掛起當前事務
9 try{ 10 tm.begin();//重新開啟第二個事務
11 Transaction ts2 = tm.getTransaction(); 12 methodB(); 13 ts2.commit();//提交第二個事務
14 } Catch(RunTimeException ex) { 15 ts2.rollback();//回滾第二個事務
16 } finally { 17 //釋放資源
18 } 19 //methodB執行完后,恢復第一個事務
20 tm.resume(ts1); 21 doSomeThingB(); 22 ts1.commit();//提交第一個事務
23 } catch(RunTimeException ex) { 24 ts1.rollback();//回滾第一個事務
25 } finally { 26 //釋放資源
27 }
在這里,我把ts1稱為外層事務,ts2稱為內層事務。從上面的代碼可以看出,ts2與ts1是兩個獨立的事務,互不相干。Ts2是否成功並不依賴於 ts1。如果methodA方法在調用methodB方法后的doSomeThingB方法失敗了,而methodB方法所做的結果依然被提交。而除了 methodB之外的其它代碼導致的結果卻被回滾了
5、PROPAGATION_NOT_SUPPORTED 以非事務方式運行,如果有事務存在,掛起當前事務
6、PROPAGATION_NEVER 以非事務方式運行,如果有事務存在,拋出異常
7、PROPAGATION_NESTED 如果當前事務存在,則嵌套事務執行
1 @Transactional(propagation = Propagation.REQUIRED) 2 methodA(){ 3 doSomeThingA(); 4 methodB(); 5 doSomeThingB(); 6 } 7
8 @Transactional(propagation = Propagation.NESTED) 9 methodB(){ 10 …… 11 }
如果單獨調用methodB方法,則按REQUIRED屬性執行。如果調用methodA方法,相當於下面的效果
1 Connection con = null; 2 Savepoint savepoint = null; 3 try{ 4 con = getConnection(); 5 con.setAutoCommit(false); 6 doSomeThingA(); 7 savepoint = con2.setSavepoint(); 8 try{ 9 methodB(); 10 } catch(RuntimeException ex) { 11 con.rollback(savepoint); 12 } finally { 13 //釋放資源
14 } 15 doSomeThingB(); 16 con.commit(); 17 } catch(RuntimeException ex) { 18 con.rollback(); 19 } finally { 20 //釋放資源
21 }
當methodB方法調用之前,調用setSavepoint方法,保存當前的狀態到savepoint。如果methodB方法調用失敗,則恢復到之前保存的狀態。但是需要注意的是,這時的事務並沒有進行提交,如果后續的代碼(doSomeThingB()方法)調用失敗,則回滾包括methodB方法的所有操作。嵌套事務一個非常重要的概念就是內層事務依賴於外層事務。外層事務失敗時,會回滾內層事務所做的動作。而內層事務操作失敗並不會引起外層事務的回滾。