學習《spring 3.x企業應用開發實戰》之Spring的事務管理
阿新 • • 發佈:2019-02-06
1、資料庫事務基礎知識
1.1、資料庫事務的概念
(1)資料庫事物的4個特性(ACID):
不可重複讀和幻讀可理解為多次訪問的不冪等
:
1.4、事務隔離級別 儘管資料庫為使用者提供了鎖的DML操作方式,但直接使用鎖管理是非常麻煩的,因此資料庫為使用者提供了自動鎖機制。 只要使用者指定會話的事務隔離級別,資料庫就會分析事務中的SQL語句,然後自動為事務操作的資料資源加上合適的鎖。此外資料庫還會維護這些鎖,當一個資源上鎖數目太多時,自動進行鎖升級以提高系統的執行效能。 (1)ANSI/ISO SQL 92標準定義了4個等級的事務隔離級別:
(2)我們一般採用讀提交的方式
1.5、JDBC對事務支援
(1)不是所有的資料庫都支援事務,也不是所有支援事務的資料庫都支援所有的事務隔離級別。使用者可以通過Connection的getMetaData()方法獲取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的介面方法
其中,TransactionDefinition用於描述事務的隔離級別,超時時間、是否為只讀事務和事務傳播規則等控制事務具體行為的事務屬性。這些事務屬性可以通過XML配置、註解描述或手工程式設計的方式設定。PlatformTransactionManager根據TransactionDefinition提供的事務屬性配置資訊建立事務,並用TransactionStatus描述這個啟用事務的狀態。 (2)TransactionDefinition
其中,SavepointManager介面擁有以下的方法:
這些事務管理器都是對特定事務實現框架的代理,這樣我們就可以通過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中獲取對應執行緒繫結資源的工具類,如圖所示:
當使用PROPAGATION_NESTED時,底層的資料來源必須基於JDBC3.0,並且實現者需要支援儲存點事務機制。 4、程式設計式的事務管理 Spring為程式設計式事務管理提供了模板類org.springframework.transaction.support.TransactionTemplate,和那些持久化模板類一樣,TransactionTemplate也是執行緒安全的。TransactionTemplate有2個重要的方法:
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個屬性:
@Transactional有一組普適性的預設事務屬性:
(6)使用不同的事務管理器
6.2、通過引入AspectJ LTW在類載入期引入事務切面 7、整合特定的應用伺服器 一般來說Spring事務抽象與應用伺服器是無關的,如果使用者希望事務管理器使用特定的UserTransaction和TransactionManager物件,以獲取更多的事務控制功能,這時可以採用Spring為整合這些應用伺服器所提供的介面卡。
- 原子性(Atomic):表示組成一個事務的多個數據庫操作是一個不可分割的原子單元,所有的操作要麼全部成功,要麼全部失敗。
- 一致性(Consistency):事務操作成功之後,資料庫所處的狀態和它的業務規則是一致的,即資料不會被破壞。
- 隔離性(Isolation):在併發資料操作時,不同的事務擁有各自的資料空間,它們的操作不會對對方產生干擾。準確地說,並非要求做到完全無干擾,資料庫規定了多種事務隔離級別,不同的隔離級別對應不同的干擾程度,隔離級別越高,資料一致性越好,但併發性越弱。
- 永續性(Durablity):一旦事務提交成功,事務中所有的資料操作都必須被持久化到資料庫中。
- 資料庫管理系統一般採用重執行日誌保證原子性、一致性和永續性,重執行日誌記錄了資料庫變化的每一個動作。
- 在一個事務中執行一部分操作發生錯誤退出,資料庫可以根據重執行日誌撤銷已經執行的操作。
- 對於已經提交的事務,即使資料庫崩潰,在重啟資料庫時也能夠根據日誌對尚未持久化的資料進行相應的重執行操作。
- 髒讀(dirty read):A事務讀取B事務尚未提交的更改資料。
- 不可重複讀(unrepeatable read):A事務讀取了B事務已經提交的更改資料,即A事務在2次讀取的資料不一致。
- 幻讀(plantom read):A事務讀取B事務提交的新增資料,幻讀一般發生在計算統計資料的事務中。
- 第一類丟失更新:A事務撤銷時,把已經提交的B事務的更新資料覆蓋了。
- 第二類丟失更新:A事務覆蓋B事務已經提交的資料,造成B事務所做的操作丟失。
- 共享鎖定會防止獨佔鎖定,但允許其他的共享鎖定;
- 獨佔鎖定既防止其他的獨佔鎖定,也防止其他的共享鎖定。
1.4、事務隔離級別 儘管資料庫為使用者提供了鎖的DML操作方式,但直接使用鎖管理是非常麻煩的,因此資料庫為使用者提供了自動鎖機制。 只要使用者指定會話的事務隔離級別,資料庫就會分析事務中的SQL語句,然後自動為事務操作的資料資源加上合適的鎖。此外資料庫還會維護這些鎖,當一個資源上鎖數目太多時,自動進行鎖升級以提高系統的執行效能。 (1)ANSI/ISO SQL 92標準定義了4個等級的事務隔離級別:
隔離級別 | 髒讀 | 不可重複讀 | 幻讀 | 第一類更新丟失 | 第二類更新丟失 |
讀未提交 | √ |
√ |
√ |
× |
√ |
讀提交 |
× |
√ |
√ |
× |
√ |
可重複讀 |
× |
× |
√ |
× |
× |
序列化 |
× |
× |
× |
× |
× |
(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():返回當前執行緒的執行緒區域性變數的初始值,
其中,TransactionDefinition用於描述事務的隔離級別,超時時間、是否為只讀事務和事務傳播規則等控制事務具體行為的事務屬性。這些事務屬性可以通過XML配置、註解描述或手工程式設計的方式設定。PlatformTransactionManager根據TransactionDefinition提供的事務屬性配置資訊建立事務,並用TransactionStatus描述這個啟用事務的狀態。 (2)TransactionDefinition
- 事務隔離:TransactionDefinition使用了java.sql.Connection介面中同名的4個隔離級別,此外,TransactionDefinition還定義了一個預設的隔離級別,它表示使用底層資料庫的預設隔離級別。
- 事務傳播:通常在一個事務中執行的所有程式碼都會同一事務的上下文中。但是Spring也提供了幾個可選的事務傳播型別,例如簡單地參與到現有的事務中,或者掛起當前的事務,建立一個新事務。
- 事務超時:超過時間後,事務被回滾。有些事務管理器不支援事務過期的功能,這時如果設定TIMEOUT_DEFAULT等值時將丟擲異常。
- 只讀狀態:只讀事務不修改任何資料,主要用於優化,如果更改資料就會丟擲異常。
其中,SavepointManager介面擁有以下的方法:
- Object createSavepoint():建立一個儲存點物件,以便在後面可以利用rollbackToSavepoint(Object savepoint)方法使事務回滾到特定的儲存點上,也可以通過releaseSavepoint()釋放一個已經不用的儲存點。
- void rollbackToSavepoint(Object savepoint):將事務回滾到特定儲存點上,被回滾的儲存點將自動釋放。
- void releaseSavepoint(Object savepoint):釋放一個儲存點,如果事務提交,所有儲存點會被自動釋放,無須手工清除。
- boolean hasSavepoint():判斷當前的事務是否在內部建立了一個儲存點,儲存點是為了支援Spring的巢狀事務而建立的。
- boolean isNewTransaction():判斷當前事務是否是一個新的事務,如果返回false,表示當前事務是一個已經存在的事務,或者當前操作未執行在事務環境中。
- boolean isCompleted():判斷當前事務是否已經結束(已經提交或回滾)。
- boolean isRollbackOnly():判斷當前事務是否已經被標識為rollback-only。
- void setRollbackOnly():將當前的事務設定為rollback-only,通過該標識通知事務管理器只能將事務回滾,事務管理器將顯式呼叫回滾命令或以丟擲異常的方式回滾事務。
- TransactionStatus getTransaction(TransactionDefinition definition):該方法根據事務定義資訊從事務環境中返回一個已存在的事務,或者建立一個新的事務,並用TransactionStatus描述這個事務的狀態。
- commit(TransactionStatus status):根據事務的狀態提交事務,如果事務狀態已經被標識為rollback-only,該方法將執行一個回滾事務的操作。
- rollback(TransactionStatus status):回滾事務,當提交事務丟擲異常時,回滾會被隱式執行。
這些事務管理器都是對特定事務實現框架的代理,這樣我們就可以通過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異常。
當使用PROPAGATION_NESTED時,底層的資料來源必須基於JDBC3.0,並且實現者需要支援儲存點事務機制。 4、程式設計式的事務管理 Spring為程式設計式事務管理提供了模板類org.springframework.transaction.support.TransactionTemplate,和那些持久化模板類一樣,TransactionTemplate也是執行緒安全的。TransactionTemplate有2個重要的方法:
- void setTransactionManager(PlatformTransactionManager transactionManager):設定事務管理器。
- Object execute(TransactionCallback action):在TransactionCallback回撥介面中定義需要以事務的方式組織的資料訪問邏輯。
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:如果業務類除事務切面外,還需要織入其他的切面,通過該屬性可以控制事務切面在目標連線點的織入順序。
@Transactional有一組普適性的預設事務屬性:
- 事務傳播行為:PROPAGATION_REQUIRED;
- 事務隔離級別:ISOLATION_DEFAULT;
- 讀寫事務屬性:讀/寫事務;
- 超時時間:依賴於底層的事務系統的預設值;
- 回滾設定:任何執行期異常引發回滾,任何檢查型異常不會引發回滾。
- 如果不設定rollbackFor,那麼只有執行期異常會被回滾,檢查型異常不會回滾;
- rollbackForClassName是萬用字元匹配,即只要有幾個字元匹配就行;
- 就算指定了rollbackForClassName的檢查型異常,程式遇到執行期異常,依然會引發回滾。
(6)使用不同的事務管理器
6.2、通過引入AspectJ LTW在類載入期引入事務切面 7、整合特定的應用伺服器 一般來說Spring事務抽象與應用伺服器是無關的,如果使用者希望事務管理器使用特定的UserTransaction和TransactionManager物件,以獲取更多的事務控制功能,這時可以採用Spring為整合這些應用伺服器所提供的介面卡。