1. 程式人生 > >學習《spring 3.x企業應用開發實戰》之Spring的事務管理

學習《spring 3.x企業應用開發實戰》之Spring的事務管理

1、資料庫事務基礎知識 1.1、資料庫事務的概念 (1)資料庫事物的4個特性(ACID):
  • 原子性(Atomic):表示組成一個事務的多個數據庫操作是一個不可分割的原子單元,所有的操作要麼全部成功,要麼全部失敗。
  • 一致性(Consistency):事務操作成功之後,資料庫所處的狀態和它的業務規則是一致的,即資料不會被破壞。
  • 隔離性(Isolation):在併發資料操作時,不同的事務擁有各自的資料空間,它們的操作不會對對方產生干擾。準確地說,並非要求做到完全無干擾,資料庫規定了多種事務隔離級別,不同的隔離級別對應不同的干擾程度,隔離級別越高,資料一致性越好,但併發性越弱。
  • 永續性(Durablity):一旦事務提交成功,事務中所有的資料操作都必須被持久化到資料庫中。
在這些特性中,資料“一致性”是最終目標,其他屬性都是為達到這一目標的方式手段。 (2)重執行日誌
  • 資料庫管理系統一般採用重執行日誌保證原子性、一致性和永續性,重執行日誌記錄了資料庫變化的每一個動作。
  • 在一個事務中執行一部分操作發生錯誤退出,資料庫可以根據重執行日誌撤銷已經執行的操作。
  • 對於已經提交的事務,即使資料庫崩潰,在重啟資料庫時也能夠根據日誌對尚未持久化的資料進行相應的重執行操作。
(3)隔離性 和Java程式採用物件鎖機制進行執行緒同步類似,資料庫管理系統採用資料庫鎖機制保證事務的隔離性。Oracle資料庫還採用了資料版本的機制,在回滾段為資料的每個變化都儲存一個版本,使資料的更改不影響資料的讀取。 1.2、資料併發的問題
不可重複讀和幻讀可理解為多次訪問的不冪等
  • 髒讀(dirty read):A事務讀取B事務尚未提交的更改資料。
  • 不可重複讀(unrepeatable read):A事務讀取了B事務已經提交的更改資料,即A事務在2次讀取的資料不一致。
  • 幻讀(plantom read):A事務讀取B事務提交的新增資料,幻讀一般發生在計算統計資料的事務中。
  • 第一類丟失更新:A事務撤銷時,把已經提交的B事務的更新資料覆蓋了。
  • 第二類丟失更新:A事務覆蓋B事務已經提交的資料,造成B事務所做的操作丟失。
1.3、資料庫鎖機制 (1)按鎖定的物件不同,分為表鎖定行鎖定。 (2)從併發事務鎖定的關係上看,可以分為共享鎖定獨佔鎖定
  • 共享鎖定會防止獨佔鎖定,但允許其他的共享鎖定;
  • 獨佔鎖定既防止其他的獨佔鎖定,也防止其他的共享鎖定。
為了更改資料,資料庫必須在進行更改的行上施加行獨佔鎖定,insert、update、delete和select for update語句都會隱式採用必要的行鎖定。 (3)下面介紹一下Oracle資料庫常用的5種鎖定:


1.4、事務隔離級別 儘管資料庫為使用者提供了鎖的DML操作方式,但直接使用鎖管理是非常麻煩的,因此資料庫為使用者提供了自動鎖機制。 只要使用者指定會話的事務隔離級別,資料庫就會分析事務中的SQL語句,然後自動為事務操作的資料資源加上合適的鎖。此外資料庫還會維護這些鎖,當一個資源上鎖數目太多時,自動進行鎖升級以提高系統的執行效能。 (1)ANSI/ISO SQL 92標準定義了4個等級的事務隔離級別:
隔離級別 髒讀 不可重複讀 幻讀 第一類更新丟失 第二類更新丟失
讀未提交

×
讀提交 ×


×

可重複讀 ×
×

×
×
序列化 ×
×
×
×
×
(2)我們一般採用讀提交的方式 1.5、JDBC對事務支援 (1)不是所有的資料庫都支援事務,也不是所有支援事務的資料庫都支援所有的事務隔離級別。使用者可以通過ConnectiongetMetaData()方法獲取DatabaseMetaData物件,並通過該物件的supportsTransactions()方法和supportsTransactionIsolationLevel(int level)方法檢視底層資料庫的事務支援情況。 (2)Connection預設情況是自動提交事務的,即每條sql都對應一個事務,為了將多條sql當成一個事務執行,必須通過Connection#setAutoCommit(false)阻止Connection自動提交,並通過Connection#setTransactionIsolation()設定事務的隔離級別,下面是典型的JDBC事務資料操作程式碼:

(3)在JDBC2.0中,事務只有2個操作:提交和回滾。JDBC3.0以後,引入了儲存點的特性,Savepoint介面允許使用者將事務分割為多個階段,使用者可以指定回滾到事務的特定儲存點。一個使用的例子如下:

並非所有的資料庫都支援儲存點功能,使用者可以通過DatabaseMetaData#savePoints()方法方法檢視是否支援。 2、ThreadLocal基礎知識 前面我們知道,Spring通過各種模板類降低了開發者使用各種資料持久化技術的難度。這些模板類都是執行緒安全的,也就是說多個DAO可以複用同一個模板例項而不會發生衝突,Spring通過ThreadLocal解決了模板類繫結資料連線和會話資源時的併發問題。 2.1、ThreadLocal是什麼 (1)ThreadLocal的介面方法
  • void  set(Object  value):設定當前執行緒的執行緒區域性變數。
  • public  Object  get():返回當前執行緒的執行緒區域性變數。
  • public  void  remove():刪除當前執行緒的執行緒區域性變數,目的是為了減少記憶體佔用。需要注意的是,當執行緒結束後,對應該執行緒的區域性變數將自動被垃圾回收,所以顯式地呼叫次方法不是必須的,但是可以加快記憶體回收的速度。
  • protected  Object  initialValue():返回當前執行緒的執行緒區域性變數的初始值,
jdk5.0以後,ThreadLocal已經支援泛型了,變為ThreadLocal<T>。 (2)ThreadLocal是如何做到為每一個執行緒維護一份獨立的變數副本呢? 其實實現思路很簡單:在ThreadLocal中有一個Map,用於儲存每一個執行緒的變數副本,Map中元素的key為執行緒物件,而value就是對應執行緒的變數副本。 2.2、與Thread同步機制的比較 ThreadLocal採用“以空間換時間”的方式:訪問並行化,物件獨享化。 2.3、Spring使用ThreadLocal解決執行緒安全問題 在Spring中,絕大部分Bean都可以宣告為singleton,就是因為Spring對一些Bean採用了ThreadLocal進行封裝。一般情況下從接收請求到返回響應所經過的所有程式呼叫都同屬於一個執行緒。 3、Spring對事務管理的支援 Spring為事務管理提供了一致的程式設計模板,在高層次建立了統一的事務抽象。像Spring DAO為不同的持久化技術實現提供模板類一樣,Spring事務管理繼承了這一風格,也提供了事務模板類TransactionTemplate。通過TransactionTemplate並配合使用事務回撥TransactionCallback指定具體的持久化操作就可以通過程式設計方式實現事務管理,而無須關注資源獲取、複用、釋放、事務同步和異常處理的操作。 3.1、事務管理關鍵抽象 (1)在Spring事務管理SPI的抽象層主要包括3個介面,分別是PlatformTransactionManagerTransactionDefinitionTransactionStatus,它們位於org.springframework.transaction包中。3者關係如圖:
其中,TransactionDefinition用於描述事務的隔離級別,超時時間、是否為只讀事務和事務傳播規則等控制事務具體行為的事務屬性。這些事務屬性可以通過XML配置、註解描述或手工程式設計的方式設定。PlatformTransactionManager根據TransactionDefinition提供的事務屬性配置資訊建立事務,並用TransactionStatus描述這個啟用事務的狀態。 (2)TransactionDefinition
  • 事務隔離:TransactionDefinition使用了java.sql.Connection介面中同名的4個隔離級別,此外,TransactionDefinition還定義了一個預設的隔離級別,它表示使用底層資料庫的預設隔離級別。
  • 事務傳播:通常在一個事務中執行的所有程式碼都會同一事務的上下文中。但是Spring也提供了幾個可選的事務傳播型別,例如簡單地參與到現有的事務中,或者掛起當前的事務,建立一個新事務。
  • 事務超時:超過時間後,事務被回滾。有些事務管理器不支援事務過期的功能,這時如果設定TIMEOUT_DEFAULT等值時將丟擲異常。
  • 只讀狀態:只讀事務不修改任何資料,主要用於優化,如果更改資料就會丟擲異常。
(3)TransactionStatus ①TransactionStatus代表一個事務的具體執行狀態,事務管理器通過該介面獲取事務的執行期狀態資訊,也可以通過該介面間接地回滾事務,它相比於在丟擲異常時回滾事務的方式更具有可控性。該介面繼承於SavepointManager介面,SavepointManager介面基於JDBC3.0儲存點的分段事務控制能力提供了巢狀事務的機制。
其中,SavepointManager介面擁有以下的方法:
  • Object  createSavepoint():建立一個儲存點物件,以便在後面可以利用rollbackToSavepoint(Object  savepoint)方法使事務回滾到特定的儲存點上,也可以通過releaseSavepoint()釋放一個已經不用的儲存點。
  • void  rollbackToSavepoint(Object  savepoint):將事務回滾到特定儲存點上,被回滾的儲存點將自動釋放。
  • void  releaseSavepoint(Object  savepoint):釋放一個儲存點,如果事務提交,所有儲存點會被自動釋放,無須手工清除。
這3個方法在底層資源不支援儲存點時,都將丟擲NestedTransactionNotSupportedException異常。 ②TransactionStatus擴充套件了SavepointManager並提供了以下方法:
  • boolean  hasSavepoint():判斷當前的事務是否在內部建立了一個儲存點,儲存點是為了支援Spring的巢狀事務而建立的。
  • boolean  isNewTransaction():判斷當前事務是否是一個新的事務,如果返回false,表示當前事務是一個已經存在的事務,或者當前操作未執行在事務環境中。
  • boolean  isCompleted():判斷當前事務是否已經結束(已經提交或回滾)。
  • boolean  isRollbackOnly():判斷當前事務是否已經被標識為rollback-only。
  • void  setRollbackOnly():將當前的事務設定為rollback-only,通過該標識通知事務管理器只能將事務回滾,事務管理器將顯式呼叫回滾命令或以丟擲異常的方式回滾事務。
(4)PlatformTransactionManager PlatformTransactionManager是事務的最高層抽象,它提供了3個介面方法:
  • TransactionStatus  getTransaction(TransactionDefinition  definition):該方法根據事務定義資訊從事務環境中返回一個已存在的事務,或者建立一個新的事務,並用TransactionStatus描述這個事務的狀態。
  • commit(TransactionStatus  status):根據事務的狀態提交事務,如果事務狀態已經被標識為rollback-only,該方法將執行一個回滾事務的操作。
  • rollback(TransactionStatus  status):回滾事務,當提交事務丟擲異常時,回滾會被隱式執行。
3.2、Spring的事務管理器實現類 (1)Spring將事務管理委託給底層具體的持久化實現框架完成,因此Spring為不同的持久化框架提供了PlatformTransactionManager介面的實現類,如下圖:

這些事務管理器都是對特定事務實現框架的代理,這樣我們就可以通過spring的高階抽象,對不同種類的事務實現使用相同的方式進行管理。 (2)Spring JDBC和iBatis 配置如下: <bean    id="dataSource"    class="org.apache.commons.dbcp.BasicDataSource"    destory-method="close"         p:driverClassName="com.mysql.jdbc.Driver"         p:url="jdbc:mysql://資料庫伺服器ip:埠/資料庫例項名"         p:username="資料庫使用者名稱"         p:password="密碼"/> <!-- 基於資料來源的事務管理器 --> <bean    id="transactionManager"            class="org.springframework.jdbc.datasource.DataSourceTransactionManager"         p:dataSource-ref="dataSource"/> 在幕後,DataSourceTransactionManager使用DataSource的Connection的commit()、rollback()等方法管理事務。 (3)JPA     JPA通過javax.persistence.EntityTransaction管理JPA的事務,EntityTransaction物件可以通過javax.persistence.EntityManager#getTransaction()獲得,而EntityManager又通過一個工廠類方法獲取javax.persistence.EntityManagerFactory#createEntityManager()。     在底層,JPA依然通過JDBC的Connection的事務方法完成最終的控制,因此要配置一個JPA事務管理器,還要先提供一個DataSource,然後配置一個EntityManagerFactory,最後才配置JpaTransactionManager。 配置檔案如下: ...... <bean    id="entityManagerFactory"    class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean"         p:dataSource-ref="dataSource"/> <bean    id="transactionManager"    class="org.springframework.orm.jpa.JpaTransactionManager"         p:entityManagerFactory-ref="entityManagerFactory"/>
(4)Hibernate Spring3.x取消了對Hibernate 2.x的支援,只支援Hibernate 3.x。 配置如下:
(5)JTA 如果希望在Java EE容器裡使用JTA,我們將通過JNDI和Spring的JtaTransactionManager獲取一個容器管理的DataSource。JtaTransactionManager不需要知道DataSource和其他特定的資源,因為它引用容器提供的全域性事務管理。 配置如下:

3.3、事務同步管理器     Spring將JDBC的Connection、Hibernate的Session等訪問資料庫的連線或會話物件統稱為資源。這些資源在同一時刻是不能多執行緒共享的,為了讓DAO、Service能做到singleton,Spring的事務同步管理器類org.springframework.transaction.support.TransactionSynchronizationManager使用ThreadLocal為不同事務執行緒提供了獨立的資源副本,同時維護事務配置的屬性和執行狀態資訊。     Spring框架為不同的持久化技術提供了一套從TransactionSynchronizationManager中獲取對應執行緒繫結資源的工具類,如圖所示:

  • 當需要脫離模板類,手工操作底層持久化技術的原生API時,就需要通過這些工具類獲取執行緒繫結的資源,而不應該直接從DataSource或SessionFactory中獲取。因為後者不能獲得和本執行緒相關的資源。
  • 這些工具類還有另外一個重要的用途:將特定異常轉換為Spring的DAO異常。
3.4、事務傳播行為 (1)在一個service介面中可能會呼叫另一個service介面的方法,以共同完成一個完整的業務操作,Spring通過事務傳播行為控制當前的事務如何傳播到被巢狀呼叫的目標服務介面方法中。Spring在TransactionDefinition介面中規定了7種類型的事務傳播行為,如下圖:
當使用PROPAGATION_NESTED時,底層的資料來源必須基於JDBC3.0,並且實現者需要支援儲存點事務機制。 4、程式設計式的事務管理 Spring為程式設計式事務管理提供了模板類org.springframework.transaction.support.TransactionTemplate,和那些持久化模板類一樣,TransactionTemplate也是執行緒安全的。TransactionTemplate有2個重要的方法:
  • void  setTransactionManager(PlatformTransactionManager  transactionManager):設定事務管理器。
  • Object  execute(TransactionCallback  action):在TransactionCallback回撥介面中定義需要以事務的方式組織的資料訪問邏輯。
TransactionCallback介面只有一個方法:Object  doInTransaction(TransactionStatus  status)。如果操作不會返回結果,可以使用TransactionCallback的子介面TransactionCallbackWithoutResult。
5、使用XML配置宣告式事務 5.1、使用原始的TransactionProxyFactoryBean 在早期的版本中,spring要求使用者通過TransactionProxyFactoryBean對需要事務管理的業務類進行代理。現在已經不推薦使用,但是仍然可用。 (1)宣告式事務配置 使用TransactionProxyFactoryBean配置事務如下:

(2)異常回滾/提交規則 上圖<prop/>內的值為事務屬性資訊,其配置格式如下:

使用者可以通過顯式指定回滾規則:通過指定帶正號(+)或負號(-)的異常類名(或異常名匹配片段),並且Exception可以配置多個。 5.2、基於tx/aop名稱空間的配置 下面是通過tx/aop配置事務的示例:


如果事務管理器命名為transactionManager,則<tx:advice/>可以不指定transaction-manager屬性。 <tx:method/>替代了之前TransactionProxyFactoryBean使用逗號分隔的方式配置事務屬性,如果需要為不同的業務Bean採取不同的事務屬性配置,則可以在<aop:config/>中定義多個切面。<tx:method/>屬性表如下:

使用aop/tx配置的宣告式事務管理是使用最廣泛的,因為它的表達能力最強,且使用靈活。 6、使用註解配置宣告式事務 6.1、使用@Transactional註解 (1)@Transactional可以用於標註類和類的public方法,當然,註解只是元資料,它本身並不能完成事務切面織入的功能,因此我們還要在Spring配置檔案中,通過一行配置“通知”Spring對標註@Transactional的Bean進行加工處理。如下: 。。。 <!-- 對標註@Transactional的Bean進行加工處理,以織入事務管理切面 --> <tx:annotation-driven    transaction-manager="txManager"/> (2)<tx:annotation-driven/>會自動使用名為transactionManager的事務管理器,所以如果使用者的事務管理器id為transactionManager,可以進一步將上面的配置簡化為<tx:annotation-driven/>
(3)<tx:annotation-driven/>還要另外2個屬性:
  • proxy-target-class:true表示使用cglib建立代理類,false表示使用jdk建立代理類。
  • order:如果業務類除事務切面外,還需要織入其他的切面,通過該屬性可以控制事務切面在目標連線點的織入順序。
(4)@Transactional屬性說明如圖:
@Transactional有一組普適性的預設事務屬性:
  • 事務傳播行為:PROPAGATION_REQUIRED;
  • 事務隔離級別:ISOLATION_DEFAULT;
  • 讀寫事務屬性:讀/寫事務;
  • 超時時間:依賴於底層的事務系統的預設值;
  • 回滾設定:任何執行期異常引發回滾,任何檢查型異常不會引發回滾。
有2點需要注意:
  • 如果不設定rollbackFor,那麼只有執行期異常會被回滾,檢查型異常不會回滾;
  • rollbackForClassName是萬用字元匹配,即只要有幾個字元匹配就行;
  • 就算指定了rollbackForClassName的檢查型異常,程式遇到執行期異常,依然會引發回滾。
(5)在何處標註@Transactional註解 @Transactional註解可以被應用於介面和介面方法,類和類的public方法上(類級的註解適用於類中的所有public方法,方法處的註解會覆蓋類定義處的註解)。但Spring建議在業務實現類上使用@Transactional註解,因為註解不能被繼承,所以用在介面上會有隱患。即使<tx:annotation-driven/>的proxy-target-class屬性設定為true,業務類照樣工作在非事務的環境下。
(6)使用不同的事務管理器


6.2、通過引入AspectJ LTW在類載入期引入事務切面 7、整合特定的應用伺服器 一般來說Spring事務抽象與應用伺服器是無關的,如果使用者希望事務管理器使用特定的UserTransaction和TransactionManager物件,以獲取更多的事務控制功能,這時可以採用Spring為整合這些應用伺服器所提供的介面卡。