事务的属性
事務傳播屬性
當事務方法被另一個事務方法調用時, 必須指定事務應該如何傳播. 例如: 方法可能繼續在現有事務中運行, 也可能開啟一個新事務, 并在自己的事務中運行.
事務的傳播行為可以由傳播屬性指定. Spring 定義了 7? 種類傳播行為.
Spring 支持的事務傳播行為
需求
新定義 Cashier 接口: 表示客戶的結賬操作
修改數據表信息如下, 目的是用戶 Tom 在結賬時, 余額只能支付第一本書, 不夠支付第二本書:
REQUIRED 傳播行為
當 bookService 的 purchase() 方法被另一個事務方法 checkout() 調用時, 它默認會在現有的事務內運行. 這個默認的傳播行為就是 REQUIRED. 因此在 checkout() 方法的開始和終止邊界內只有一個事務. 這個事務只在 checkout() 方法結束的時候被提交, 結果用戶一本書都買不了
事務傳播屬性可以在 @Transactional 注解的 propagation 屬性中定義
REQUIRES_NEW 傳播行為
另一種常見的傳播行為是 REQUIRES_NEW. 它表示該方法必須啟動一個新事務, 并在自己的事務內運行. 如果有事務在運行, 就應該先掛起它.
在 Spring 2.x 事務通知中配置傳播屬性
在 Spring 2.x 事務通知中, 可以像下面這樣在 <tx:method> 元素中設定傳播事務屬性
并發事務所導致的問題
當同一個應用程序或者不同應用程序中的多個事務在同一個數據集上并發執行時, 可能會出現許多意外的問題
并發事務所導致的問題可以分為下面三種類型:
–?臟讀: 對于兩個事物 T1, T2, T1? 讀取了已經被 T2 更新但 還沒有被提交的字段. 之后, 若 T2 回滾, T1讀取的內容就是臨時且無效的.
–?不可重復讀:對于兩個事物 T1, T2, T1? 讀取了一個字段, 然后 T2 更新了該字段. 之后, T1再次讀取同一個字段, 值就不同了.
–?幻讀:對于兩個事物 T1, T2, T1? 從一個表中讀取了一個字段, 然后 T2 在該表中插入了一些新的行. 之后, 如果 T1 再次讀取同一個表, 就會多出幾行.
事務的隔離級別
從理論上來說, 事務應該彼此完全隔離, 以避免并發事務所導致的問題. 然而, 那樣會對性能產生極大的影響, 因為事務必須按順序運行.
在實際開發中, 為了提升性能, 事務會以較低的隔離級別運行.
事務的隔離級別可以通過隔離事務屬性指定
Spring 支持的事務隔離級別
事務的隔離級別要得到底層數據庫引擎的支持, 而不是應用程序或者框架的支持.
Oracle 支持的 2 種事務隔離級別:READ_COMMITED , SERIALIZABLE
Mysql 支持 4 中事務隔離級別.
設置隔離事務屬性
用 @Transactional 注解聲明式地管理事務時可以在 @Transactional 的 isolation 屬性中設置隔離級別.
在 Spring 2.x 事務通知中, 可以在 <tx:method> 元素中指定隔離級別
設置回滾事務屬性
默認情況下只有未檢查異常(RuntimeException和Error類型的異常)會導致事務回滾. 而受檢查異常不會.
事務的回滾規則可以通過 @Transactional 注解的 rollbackFor 和 noRollbackFor 屬性來定義. 這兩個屬性被聲明為 Class[] 類型的, 因此可以為這兩個屬性指定多個異常類.
rollbackFor:? 遇到時必須進行回滾
noRollbackFor: 一組異常類,遇到時必須不回滾
設置回滾事務屬性
在 Spring 2.x 事務通知中, 可以在 <tx:method> 元素中指定回滾規則. 如果有不止一種異常, 用逗號分隔.
超時和只讀屬性
由于事務可以在行和表上獲得鎖,? 因此長事務會占用資源, 并對整體性能產生影響.
如果一個事物只讀取數據但不做修改, 數據庫引擎可以對這個事務進行優化.
超時事務屬性: 事務在強制回滾之前可以保持多久. 這樣可以防止長期運行的事務占用資源.
只讀事務屬性: 表示這個事務只讀取數據但不更新數據, 這樣可以幫助數據庫引擎優化事務.
設置超時和只讀事務屬性
超時和只讀屬性可以在 @Transactional 注解中定義.超時屬性以秒為單位來計算.
在 Spring 2.x 事務通知中, 超時和只讀屬性可以在 <tx:method> 元素中進行指定.
package com.learn.spring.tx.service;import java.util.List;import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional;@Service public class CashierImpl implements Cashier{@Autowiredprivate BookShopService bookShopService;@Override@Transactionalpublic void checkOut(String username, List<String> isbns) {for (String isbn : isbns) {bookShopService.buyBook(username, isbn);}}} package com.learn.spring.tx.service;import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Isolation; import org.springframework.transaction.annotation.Propagation; import org.springframework.transaction.annotation.Transactional;import com.learn.spring.tx.dao.BookShopDao; import com.learn.spring.tx.exception.UserAccountException;@Service //@Transactional //對該類中所有的方法都起作用 public class BookShopServiceImpl implements BookShopService{@Autowiredprivate BookShopDao bookShopDao ; /*** 事務的屬性:* propagation:事務的傳播行為.* REQUIRED:使用調用者的事務* REQUIRES_NEW:將調用者的事務掛起,使用自己的新事務.* * isolation:事務的隔離級別,最常用的就是READ_COMMITTED* readOnly:指定事務是否為只讀. 如果是只讀事務,代表這個事務只讀取數據庫的數據.而不進行修改操作 .* 若一個事務真的是只讀取數據,就有必須要設置readOnly=true,可以幫助數據庫引擎進行優化* * rollbackFor* rollbackForClassName* noRollbackFor* noRollbackForClassName * * timeout:指定強制回滾前事務可以占用的時間。 為了避免一個事務占用過長的時間.* */@Transactional(propagation=Propagation.REQUIRES_NEW,isolation=Isolation.READ_COMMITTED,readOnly=false/*noRollbackFor={UserAccountException.class}*//*timeout=3*/)public void buyBook(String username, String isbn) {try {Thread.sleep(5000);} catch (InterruptedException e) {// TODO Auto-generated catch blocke.printStackTrace();}//1.查詢書的價格int price = bookShopDao.findBookPriceByIsbn(isbn);//2.更新書的庫存bookShopDao.updateBookStock(isbn);//3.更新用戶的余額bookShopDao.updateUserAccount(username, price); }// }?
超強干貨來襲 云風專訪:近40年碼齡,通宵達旦的技術人生總結