1. 程式人生 > >Spring4的知識應用總結(六)——Spring事務管理機制

Spring4的知識應用總結(六)——Spring事務管理機制

Spring中的事務管理

一、事務簡介

        事務管理是企業級應用程式開發中必不可少的技術,  用來確保資料的完整性和一致性. 
事務就是一系列的動作, 它們被當做一個單獨的工作單元. 這些動作要麼全部完成, 要麼全部不起作用
事務的四個關鍵屬性(ACID)
原子性(atomicity): 事務是一個原子操作, 由一系列動作組成. 事務的原子性確保動作要麼全部完成要麼完全不起作用.
一致性(consistency): 一旦所有事務動作完成, 事務就被提交. 資料和資源就處於一種滿足業務規則的一致性狀態中.
隔離性(isolation): 可能有許多事務會同時處理相同的資料, 因此每個事物都應該與其他事務隔離開來, 防止資料損壞.
永續性
(durability): 一旦事務完成, 無論發生什麼系統錯誤, 它的結果都不應該受到影響. 通常情況下, 事務的結果被寫到持久化儲存器中

Spring 中的事務管理

作為企業級應用程式框架,Spring 在不同的事務管理 API 之上定義了一個抽象層. 而應用程式開發人員不必瞭解底層的事務管理 API, 就可以使用 Spring 的事務管理機制.
Spring 既支援程式設計式事務管理, 也支援宣告式的事務管理. 
程式設計式事務管理: 將事務管理程式碼嵌入到業務方法中來控制事務的提交和回滾. 在程式設計式管理事務時, 必須在每個事務操作中包含額外的事務管理程式碼. 
宣告式事務管理: 大多數情況下比程式設計式事務管理更好用. 它將事務管理程式碼從業務方法中分離出來, 以宣告的方式來實現事務管理.
事務管理作為一種橫切關注點, 可以通過 AOP 方法模組化. Spring 通過 Spring AOP 框架支援宣告式事務管理.

Spring 中的事務管理器

Spring 從不同的事務管理 API 中抽象了一整套的事務機制.開發人員不必瞭解底層的事務 API, 就可以利用這些事務機制. 有了這些事務機制, 事務管理程式碼就能獨立於特定的事務技術了.
Spring 的核心事務管理抽象是platform TransactionManage它為事務管理封裝了一組獨立於技術的方法. 無論使用 Spring 的哪種事務管理策略(程式設計式或宣告式), 事務管理器都是必須的.

Spring 中的事務管理器的不同實現

DataSoureTransactionManage :在應用程式中只需要處理一個數據源, 而且通過 JDBC 存取Jta TransactionManage : 在 JavaEE 應用伺服器上用 JTA(Java Transaction API) 進行事務管理HibernateTransactionManage:用 Hibernate 框架存取資料庫
……
事務管理器以普通的 Bean 形式宣告在 Spring IOC 容器中

用 @Transactional 註解宣告式地管理事務

除了在帶有切入點, 通知和增強器的 Bean 配置檔案中宣告事務外, Spring 還允許簡單地用@Transactional 註解來標註事務方法. 
為了將方法定義為支援事務處理的, 可以為方法新增 @Transactional 註解. 根據 Spring AOP 基於代理機制,只能標註公有方法.
可以在方法或者類級別上新增 @Transactional 註解. 當把這個註解應用到類上時, 這個類中的所有公共方法都會被定義成支援事務處理的. 
在 Bean 配置檔案中只需要啟用<tx:annotation-driven>元素, 併為之指定事務管理器就可以了. 
如果事務處理器的名稱是 transactionManager, 就可以在<tx:annotation-driven> 元素中省略 transaction-manager 屬性. 這個元素會自動檢測該名稱的事務處理器.
@Transactional
@Override
public void purchase(String username, String isbn) {
bookShopDao.updateBookStock(isbn);
bookShopDao.updateUserAccount(username, price);
}
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">  
<property name="dataSource" ref="dataSource"></property>  
</bean>  
      
<!-- 2. 配置事務屬性 -->  
<tx:annotation-driven  transaction-manager="transactionManager"/> 

二、事務屬性

事務傳播屬性

        當事務方法被另一個事務方法呼叫時, 必須指定事務應該如何傳播. 例如: 方法可能繼續在現有事務中執行, 也可能開啟一個新事務, 並在自己的事務中執行.
事務的傳播行為可以由傳播屬性指定. Spring 定義了7  種類傳播行為.(一二兩種最常用)
(1)PROPAGATION_REQUIRED: 如果存在一個事務,則支援當前事務。如果沒有事務則開啟
(2)PROPAGATION_REQUIRES_NEW: 總是開啟一個新的事務。如果一個事務已經存在,則將這個存在的事務掛起。
(3)PROPAGATION_SUPPORTS: 如果存在一個事務,支援當前事務。如果沒有事務,則非事務的執行
(4)PROPAGATION_MANDATORY: 如果已經存在一個事務,支援當前事務。如果沒有一個活動的事務,則丟擲異常。
(5)PROPAGATION_NOT_SUPPORTED: 總是非事務地執行,並掛起任何存在的事務。
(6)PROPAGATION_NEVER: 總是非事務地執行,如果存在一個活動事務,則丟擲異常
(7)PROPAGATION_NESTED:如果一個活動的事務存在,則執行在一個巢狀的事務中. 如果沒有活動事務, 則按TransactionDefinition.PROPAGATION_REQUIRED 屬性執行

事務的隔離級別

從理論上來說, 事務應該彼此完全隔離, 以避免併發事務所導致的問題. 然而, 那樣會對效能產生極大的影響, 因為事務必須按順序執行. 
在實際開發中, 為了提升效能, 事務會以較低的隔離級別執行.
事務的隔離級別可以通過隔離事務屬性指定
1.ISOLATION_DEFAULT: 這是一個PlatfromTransactionManager預設的隔離級別,使用資料庫預設的事務隔離級別.另外四個與JDBC的隔離級別相對應
2.ISOLATION_READ_UNCOMMITTED: 這是事務最低的隔離級別,它充許令外一個事務可以看到這個事務未提交的資料。這種隔離級別會產生髒讀,不可重複讀和幻像讀。
3. ISOLATION_READ_COMMITTED: 保證一個事務修改的資料提交後才能被另外一個事務讀取。另外一個事務不能讀取該事務未提交的資料
4.ISOLATION_REPEATABLE_READ: 這種事務隔離級別可以防止髒讀,不可重複讀。但是可能出現幻像讀。它除了保證一個事務不能讀取另一個事務未提交的資料外,還保證了避免下面的情況產生(不可重複讀)。
5.ISOLATION_SERIALIZABLE這是花費最高代價但是最可靠的事務隔離級別。事務被處理為順序執行。除了防止髒讀,不可重複讀外,還避免了幻像讀。 
事務的隔離級別要得到底層資料庫引擎的支援, 而不是應用程式或者框架的支援.
Oracle 支援的 2 種事務隔離級別:READ_COMMITED , SERIALIZABLE
Mysql 支援 4 中事務隔離級別.

事務回滾屬性

預設情況下只有未檢查異常(RuntimeException和Error型別的異常)會導致事務回滾. 而受檢查異常不會.
事務的回滾規則可以通過@Transactional 註解的 rollbackFor 和 noRollbackFor 屬性來定義. 這兩個屬性被宣告為 Class[] 型別的, 因此可以為這兩個屬性指定多個異常類.
rollbackFor:  遇到時必須進行回滾
noRollbackFor: 一組異常類,遇到時必須不回滾

在 Spring 2.x 事務通知中, 可以在<tx:method> 元素中指定回滾規則. 如果有不止一種異常, 用逗號分隔.

超時和只讀屬性

由於事務可以在行和表上獲得鎖,  因此長事務會佔用資源, 並對整體效能產生影響. 
如果一個事物只讀取資料但不做修改, 資料庫引擎可以對這個事務進行優化.
超時事務屬性
: 事務在強制回滾之前可以保持多久. 這樣可以防止長期執行的事務佔用資源.
只讀事務屬性
: 表示這個事務只讀取資料但不更新資料, 這樣可以幫助資料庫引擎優化事務.
超時和只讀屬性可以在 @Transactional 註解中定義.超時屬性以秒為單位來計算.
在 Spring 2.x 事務通知中, 超時和只讀屬性可以在<tx:method>元素中進行指定.
<!-- 2. 配置事務屬性 -->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<tx:attributes>
<!-- 根據方法名指定事務的屬性 -->
<tx:method name="purchase"
propagation="REQUIRES_NEW"
isolation =“PROPAGATION_REQUIRES_NEWrollback-for="java.io.Exception"timeout="30"read-only="true"/></tx:attributes>
</tx:advice>