Spring的事務管理

Spring提供了一流的事務管理。在Spring中可以支持聲明式事務和編程式事務。 
    本章主要目標如下: 
    1,Spring事務 
    2,事務屬性 
    3,事務管理器 
    4,聲明式事務       
     
 1.1Spring的事務 
    事務管理在應用程序中起着至關重要的作用:它是一系列任務的組成工作單元,在這個工作單元中,所有的任務必須同時執行。它們只有二種可能執行結果,要麼所有任務全部執行成功,要麼所有任務全部執行失敗。 
    Spring中提供了豐富的事務管理功能,它們超過了EJB並且和EJB一樣支持聲明式事務,重要的是Spring提供了致的事務管理,它有如下優點。 
    1,爲不同的事務的API提供一致的編程模式 
    2,提供更簡單,更易地使用的編程式事務管理 
    3,支持Spring聲明事務 
    4,整合Spring對數據訪問的抽像 
     
 1.2事務的ACID特性 
    事務使用ACID特性來衡量事務的質量。介紹如下: 
    1,原子性 
        事務必須是原子的,在事務結束的時候,事務中的所有任務必須全部成功完成,否則全部失敗,事務回滾到事務開始之間的狀態。 
    2,一致性 
        事務必須保證和數據庫的一致性,即數據庫中的所有數據和現實保持一致。如果事務失敗數據必須返回到事務執行之前的狀態,反之修改數據和現實的同步。 
    3,隔離性 
        隔離性是事務與事務之間的屏障,每個事務必須與其他事務的執行結果隔離開,直到該事務執行完畢,它保證了事務的訪問的任何數據不會受其他事務執行結果的影響。 
    4,持久性 
        如果事務成功執行,無論系統發生任何情況,事務的持久性都必須保證事務的執行結果是永久的。 
  
        1.3事務之間的缺陷 
    在事務處理中有違返ACID特性的3個問題:髒讀取,不可重複讀和幻讀行。如果存在多個併發事務在運行,而這種事務操作了同一個數據來完成它們的任務,就會導致3個問題的存生。要解決它們,就必須在事務之間定義合適的隔離級別。 
    爲保證事務的完整性,必須解決事務之間可能存在的3個問題。 
    (1)髒讀取 
    當一個事務讀取了另一個事務尚未提交的更新,就叫髒讀取。在另一個事務回滾的情況下,當前事務所讀取的另一個事務的數據就是無效的。 
    (2)不可重複讀取 
    在一個事務中執行多次同樣的查詢操作,但每次查詢的結果都不一樣,就叫做不可重複讀取,通常這種情況是由於數據在二次查詢之間被另一個併發的事務所修改。 
    (3)幻影行 
    這是對事務危害最小的一個問候,它類似不可重複讀取,也是一個事務的更新結果影響到另一個事務問題。但是它不僅影響另一個事務查詢結果,而且還會使查詢語句返回一些不同的行錄行。 
    這3個問題危害程度依次爲:髒讀取最大-->不可重複讀取-->幻影行最小。 
 
          1.4事務的屬性 
    本節主要介紹將事務策略應用到方法的屬性描述,其內容包括事務的傳播行爲,事務的隔離級別,事務的只讀和超時屬性。 
    1,事務的傳播行爲 
        傳播行爲是事務應用於方法的邊界,它定義了事務的建立,暫停等行爲屬性。 
 
在Spring中共有7種,EJB CMT共6種。 
 
*PROPAGATION_MANDATORY: 
規定了方法必須在事務中運行,否則會拋出異常 
 
*PROPAGATION_NESTED: 
使方法運行在嵌套事務中,否則這個屬性和PROPAGATION_REQUIRED屬性的義相同 
 
PROPAGATION_NEVER 
使當前方法永遠不在事務中運行,否則拋出異常 
 
PROPAGATION_NOT_SUPPORTED 
定義爲當前事務不支持的方法,在該方法運行期間正在運行的事務會被暫停 
 
*PROPAGATION_REQUIRED 
規定當前的方法必須在事務中,如果沒有事務就創建一個新事務,一個新事務和方法一同開始,隨着方法的返回或拋出異常而終止 
 
*PROPAGATION_REQUIRED_NEW 
當前方法必須創建新的事務來運行,如果現存的事務正在運行就暫停它 
 
PROPAGATION_SUPPORTS 
規定當前方法支持當前事務處理,但如果沒有事務在運行就使用非事務方法執行 
 
以上定義Spring在事務中的傳播行爲分別對應EJB的事務CMT中的所有傳播行爲,其在PROPAGATION_NESTED是Spring在CMT之外定義的事務傳播行爲。 
 
  
    例如: 
    <property name="transactionAttributes"> 
            <props> 
                <prop key="query*">PROPAGATION_REQUIRED,timeout_5,readOnly</prop> 
                <prop key="insert*">PROPAGATION_REQUIRED</prop> 
                <prop key="delete*">PROPAGATION_REQUIRED</prop> 
            </props> 
        </property>     
 
     
     
    2,事務的隔離級別 
        爲解決事務之間的3個缺陷,必須在事務之間建立隔離關係來保證事務的完整性。 
    ISOLATION_DEFAULT         
        使用數據庫默認的隔離級別 
    ISOLATION_COMMITTED     
        允許讀取其他併發事務已經提交的更新(防此髒讀) 
    ISOLATION_READ_UNCOMMITTED 
        允許讀取其他併發事務還未提交的更新,會導致事務之                間的3個缺陷發生,這是速度最快的一個隔離級別,但同                時它的隔離級別也是最低 
    ISOLATION_REPEATABLE_READ 
        除非事務自身修改了數據,否則規定事務多次重複讀取        數據必須相同(防此髒讀,不可重複讀) 
    ISOLATION_SERIALIZABLE 
        這是最高的隔離級別,它可以防此髒讀,不可重複讀和        幻讀等問題,但因其侵佔式的數據記錄完全鎖定,導致 
        它影響事務的性能,成爲隔離級別中最展慢的一個。 
    注意:並不是所有的資源管理器都支持所有的隔離級別,可針對不同的資源管理使用以上的隔離級別。 
 
 
    3,事務的只讀屬性 
        在對數據庫的操作中,查詢是使用最頻繁的操作,每次執行查詢時都要從數據庫中重新讀取數據,有時多次讀取的數據都是相同的,這樣的數據操作不僅浪費了系統資源,還影響了系統速度。對訪問量大的程序來說,節省這部分資源可以大大提    升系統速度。 
        如果將事務聲明爲只讀的,那麼數據庫可以根據事務的特性優化事務的讀取操作。事務的只讀屬性需要配合事務的傳播行爲共同設置。例如: 
    <prop key="query*">PROPAGATION_REQUIRED,readOnly</prop> 
 
 
 
     
    4,事務的超時屬性    
        這個屬性和事務的只讀屬性一樣需要搭配事務的傳播行爲共同設置,它設置了事務的超時時間,事務本身可能會因某種原因很長沒有迴應,在這期間事務可能鎖定了數據庫的表格,這樣會出現嚴重的性能問題。通過設置事務的超時時間,從開始執行事務起,在規定的超時時間內如果沒有事務就將它回滾。事務的超時屬性以timeout_爲前綴和一個整型數字定義,例如: 
    <prop key="query*">PROPAGATION_REGUIRED,timeout_5,readOnly</prop> 
 
 
 
     
        1.5Spring的事務管理器 
    Spring的事務管理器有5個,都實現了PlatformTransactionManager接口,如下所示: 
 
DataSourceTransactionManager           JDBC事務管理器 
HibernateTransactionManager            Hibernate事務管理器 
JdoTransactionManager                  JDO事務管理器 
JtaTransactionManager                   JTA事務管理器      
PersistenceBrokerTransactionManager    Apache的OJB事務管理器         
<!-- 配置JDBC事務管理器 --> 
    <bean id="transactionManager" 
        class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> 
        <property name="dataSource"> 
            <ref bean="dataSource"/> 
        </property> 
    </bean> 
 
--------------------------------------------------------- 
 
<!-- 配置HibernateSessionFactory工廠 --> 
    <bean id="sessionFactory" 
        class="org.springframework.orm.hibernate3.LocalSessionFactoryBean"> 
        <property name="dataSource" ref="dataSource" /> 
        <property name="mappingResources"> 
            <list> 
                <value>tarena/hbm/catelog.hbm.xml</value> 
                <value>tarena/hbm/bookinfo.hbm.xml</value> 
                <value>tarena/hbm/userinfo.hbm.xml</value> 
                <value>tarena/hbm/order.hbm.xml</value> 
            </list> 
        </property> 
        <property name="hibernateProperties"> 
            <props> 
                <prop key="hibernate.dialect"> 
                    org.hibernate.dialect.MySQL5Dialect 
                </prop> 
                <prop key="hibernate.query.substitutions"> 
                    true 'Y', false 'N' 
                </prop> 
                <prop key="hibernate.show_sql">true</prop> 
            </props> 
        </property> 
    </bean> 
 
    <!-- 配置Hibernate事務管理器 --> 
    <bean id="transactionManager" 
        class="org.springframework.orm.hibernate3.HibernateTransactionManager"> 
        <property name="sessionFactory" ref="sessionFactory" /> 
    </bean> 
 
 
        1.6聲明式事務 
    因爲Spring中的事務是基於AOP實現的,而Spring的AOP是以方法爲單位的,所以Spring的事務屬性就對事務應用到方法上的策略描述,這些屬性爲:傳播行爲,隔離級別,只讀和超時屬性。 
    Spring的聲明式事務不涉及組建依賴關係,它通過AOP實現事務管理。Spring本身就是一個容器,相對EJB容器而言,Spring顯得更爲輕便,在使用Spring的聲明式事務時不須編寫任何代碼,便可通過實現基於容器的事務管理。Spring提供了一些可供選擇的輔助類,這些輔助類簡化了傳統的數據庫操作流程,在一定程度上節省了工作量,提高了編碼效率,所以推薦使用聲明事務。 
 
    1,優化DataSource 
        DriverManagerDataSource數據源,它在每次獲得數據連接時都創建一個新的連接對象,完全沒有緩衝能力。
        Spring沒有提供連接的實現,因爲一些開源項目已經實現了帶有連接池功能的數據源,例如Apache Commons DBCP的BasicDataSource數據源。這個數據源不僅提供了緩衝的連接池功能,而且它是一個完全輕量級的數據源。
    在Spring中的配置方法如下: 
        <bean id="dataSource" 
    class="org.apache.commons.dbcp.BasicDataSource"> 
        <property name="driverClassName"> 
            <value>org.gjt.mm.mysql.Driver</value> 
        </property> 
        <property name="url"> 
<value>jdbc:mysql://localhost:3306/dbname</value> 
        </property> 
        <property name="username"> 
            <value>root</value> 
        </property> 
        <property name="password"> 
            <value>admin</value> 
        </property> 
    </bean> 
 
 
 
    2,使用事務代理工廠 
        事務代理工廠TransactionProxyFactoryBean包括了事務攔截器,目標代理和事務的屬性設置,它配置方便,使用靈活,在聲明式事務中廣泛使用。 
    使用TransactionProxyFactoryBean需要注入它所依賴的事務管理器,設置代理的目標對象,代理對象的生成方式和事務屬性。代理對象是在目標對象上生成的包括事物和AOP切面的新對象,這個新的對象用來替代目標對象以支持事物或AOP提供的切面功能。 
    其中對象代理的生成方式可根據CGLIB生成代理,例如: 
    <property name="proxyTargetClass" value="true"/> 
    最後再介紹一個TransactionProxyFactoryBean中如何設置事務的屬性。TransactionProxyFactoryBean的transactionAttributes屬性用於指定事務的屬性,它是一個Properties類型的屬性集合,以方法名和事務屬性組成鍵值對,給不同的方法指定不同的事務屬性。例如: 
    <prop key="query*">PROPAGATION_REQUIRED,timeout_5,readOnly</prop> 
    方法名作爲聲明時,可以使用*通配符,上述代碼定義對所有以query作前綴的方法,並在定義的事務中完成操作,事務的不同屬性之間以","分隔。 
    通過一個實例介紹如何使用TransactionProxyFactoryBean完全成Spring的聲明式事務管理。 
 
 
    例子<略> 
 
************************************************************** 
<!-- 配置數據源 --> 
    <bean id="dataSource" 
class="org.apache.commons.dbcp.BasicDataSource"> 
        <property name="driverClassName"> 
            <value>org.gjt.mm.mysql.Driver</value> 
        </property> 
        <property name="url"> 
            <value>jdbc:mysql://localhost:3306/數據庫Schema</value> 
        </property> 
        <property name="username"> 
            <value>root</value> 
        </property> 
        <property name="password"> 
            <value>admin</value> 
        </property> 
    </bean> 
 
    <!-- 配置JDBC事務管理器 --> 
    <bean id="transactionManager" 
class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> 
        <property name="dataSource"> 
            <ref bean="dataSource" /> 
        </property> 
    </bean> 
 
    <!-- 配置事件代理工廠 --> 
    <bean id="transactionProxy" 
class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean"> 
        <!-- 要依賴事務管理器 --> 
        <property name="transactionManager"> 
            <ref bean="transactionManager"/> 
        </property> 
         
        <!-- 要依賴目標對象 --> 
        <property name="target"> 
            <bean id="userDAO" class="tarena.dao.UserDAO"> 
                <property name="dataSource"> 
                    <ref bean="dataSource"/> 
                </property> 
            </bean> 
        </property> 
         
        <!-- 要依賴代理方式 --> 
        <property name="proxyTargetClass" value="true"></property> 
         
        <!-- 要依賴事務屬性 --> 
        <property name="transactionAttributes"> 
            <props> 
                <prop key="query*">PROPAGATION_REQUIRED,timeout_1,readOnly</prop> 
                <prop key="insert*">PROPAGATION_REQUIRED,timeout_2</prop> 
                <prop key="delete*">PROPAGATION_REQUIRED</prop> 
            </props> 
        </property> 
    </bean> 
如下timeout_0,就會拋出事務異常,意思是查詢操作超過0秒便會拋出異常並結束事務。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章