Spring研究筆記------事務探究

    最近看了比較多Spring的東西,腦中概念知識比較雜亂,藉助週六周天的閒暇時間,寫一些內容,梳理一下本身腦中的概念,也以此做爲記錄,但願本身之後可以方便查閱,也但願可以對各位看官有所幫助。
    第一個Spring相關主題是Spring的事務管理,藉助官方文檔的一句話,Spring提供了一致的事物管理抽象模型,可以讓你們在不一樣的事務API(JTA、JDBC、Hibernate、JPA、JDO等)之間有一致的編程體驗,Spring提供了聲明式事務管理和編程事務管理兩種事務管理方式,前者借鑑了EJB CMT中事務管理方式,後者則簡化了JTA的事務異常編程模型,從這個角度來看,Spring事務提供了全面簡潔的一致性事務管理方案。
    熟悉Java EE編程的人應該知道,事務分爲全局(global)和本地(local)兩種事務;全局事務提供了在多個事務源之間操做保證原子性的能力,典型的事務源是數據庫,在一些企業集成領域還包括MQ等中間件,在Java EE體系中經過JTA來對全局事務提供支持,事務管理功能由應用服務器提供支持,在實際操做時須要經過JNDI進行事務對象的引用,這就存在兩種耦合綁定,特定查找協議和特定應用容器綁定,這是否是不良的組織形式要看應用的需求,但在大多數時候,大多數應用爲了獲取全局事務的能力而付出這種耦合的代價都是比較不合算的;本地事務是資源特定的,例如使用JDBC connection進行的事務操做,本地事務中的事物管理功能是應用程序進行控制的,非容器託管,所以其能擺脫容器綁定的反作用,但其侷限性也很明顯,由於是資源特定的,其不能支持跨事務源的操做,所幸咱們大部分應用都沒有跨事務源的需求,本地事務在這種狀況下比較適用;關於全局事務這裏多補充一點,由於分佈式事務自己的複雜性,其比較重量級,考慮的事務模型可能也比較複雜,除非必須使用,不然仍是敬而遠之爲好,即便由於水平擴展、高可用、負載均衡等需求而須要添置額外的事務源,也應該儘可能避免,例如使用Oracle RAC,將分佈式事務的複雜性封裝在數據庫產品層面,簡化應用級別的事務操做;實際上,全局事務操做不必定非須要綁定應用容器,藉助獨立的事務管理器如 Atomikos Transactions   JOTM也能提供JTA全局事務的功能,固然,仍是如上所述,並不推薦使用。
    Spring提供了一致的事務抽象,而這抽象的核心就是PlatformTransactionManager接口,接口定義以下
    
該接口有三個方法,第一個方法根據TransationDefinition對象獲取事務(TransactionStatus),TransationDefinition也是一個接口,定義以下
     

經過這個定義,咱們知道TransationDefinition提供了事務傳播策略以及事務隔離級別的常量定義,同時提供了默認隔離和默認事務超時的常量定義,方法上提供了獲取事務隔離級別、事務傳播策略、事務超時時間、是否只讀事務以及事務名稱信息,基本上事務的基本概念都包含在了這個接口定義中;TransactionStatus提供了簡單的控制事務以及查詢事務狀態的方法,定義以下 spring


由於接口定義比較明瞭,這裏再也不展開贅述; PlatformTransactionManager接口是Spring事務管理的核心,其類級結構以下

經過上面的結構圖,咱們看到Spring爲每一種經常使用的具體的事務操做API提供了具體的實現,例如咱們經常使用的基於純JDBC的DataSourceTransactionManager和經常使用ORM hibernate的HibernateTransactionManager,JTA API特供了通用的實現以及針對Weblogic、WebSphere應用服務器提供的特定實現; PlatformTransactionManager定義了基本的事務操做模型,而AbstractPlatformTransactionManager抽象類實現了該接口,該抽象類定義了一些全部具體實現類共有的屬性和方法,同時定義了統一的事務處理流程,這是設計模式模板模式很是經典的應用,由於該類方法定義較多,這裏咱們就不在貼出該類的定義,咱們來看看該類如何定義了統一的事務處理流程,該類實現了PlatformTransactionManager接口的commit方法,同時將該方法設爲final,使其子類不可以重寫,commit方法以下:

該方法定義了基本的提交處理,咱們看到實際處理是processXXX等方法,processCommit部分代碼以下

咱們看到這個方法負責了具體事務提交的相關操做,最後實際提交操做是doCommit,而doCommit爲一個抽象方法,這就是該類給子類實現留下的「鉤子」,具體的提交操做由子類來去實現,這是一個典型的模板模式應用,接下來咱們找一個具體子類來看看doCommit的實際操做,咱們看看比較熟悉的HibernateTransactionManager,doCommit以下:

很明顯,看到了Hibernate事務操做典型的用法,其餘實現相似,這裏咱們不在贅述,你們能夠本身看看。
        關於具體的Spring事務配置,雖然比較基礎,但我仍是寫在這裏吧,在事務配置過程當中,我穿插着描述一些原理性的東西,由於頭腦比較混亂,條理不清,還望你們海涵。
        事務管理配置的核心是各類具體 PlatformTransactionManager的配置,其次就是各類事務管理器如何做用在具體操做方法上的切面配置,由於事務管理器到具體方法的切面配置都是一致的,這裏咱們給出各類PlatformTransactionManager的配置,最後在來一個具體的事務切面配置,這裏咱們提供DataSourceTransactionManager、JtaTransactionManager以及HibernateTransactionManager的配置,關於用XML配置仍是註解配置,這裏咱們先提供XML的配置,而後在後面給出一個徹底註解的配置實例。
PlatformTransactionManager配置
1. DataSourceTransactionManager
首先須要配置JDBC的DataSource,畢竟這些本地事務是資源特定的嘛,DataSource定義以下
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
    <property name="driverClassName" value="${jdbc.driverClassName}" />
    <property name="url" value="${jdbc.url}" />
    <property name="username" value="${jdbc.username}" />
    <property name="password" value="${jdbc.password}" />
</bean>
接着就是事務管理器的配置,DataSourceTransactionManager定義以下
<bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>

這裏事務管理器就配置完了,很是簡單。
2. JtaTransactionManager
JtaTransactionManager配置更爲簡單,由於其是全局事務,爲容器託管事務,其不須要知道特定的資源(DataSource),配置以下
<bean id="txManager" class="org.springframework.transaction.jta.JtaTransactionManager" />
可是注意JtaTransactionManager不須要知道DataSource,並不意味着不須要配置,Spring Data模塊對資源的操做仍是須要DataSource的,在這種場景下DataSource由應用容器託管,因此使用JNDI進行查找引用,以下
<jee:jndi-lookup id="dataSource" jndi-name="jdbc/jpetstore"/>
3.HibernateTransactionManager
Hibernate的配置比較多一些,首先是配置DataSource,和1中一致,這裏再也不贅述,接着就是SessionFactory的配置,配置以下
<bean id="sessionFactory" class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
    <property name="dataSource" ref="dataSource" />
    <property name="mappingResources">
        <list>
            <value>org/springframework/samples/petclinic/hibernate/petclinic.hbm.xml</value>
        </list>
    </property>
    <property name="hibernateProperties">
        <value>
            hibernate.dialect=${hibernate.dialect}
        </value>
       </property>
</bean>

注意這裏的配置比較老,若是使用基於註解的實體配置,請將mappingResources替換爲
<property name="packagesToScan">
   <list>
    <value>com.app</value>
   </list>
</property>
接着就是HibernateTransactionManager的配置了,配置以下
<bean id="txManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager">
    <property name="sessionFactory" ref="sessionFactory" />
</bean>


    注意實際的持久化技術例如JDBC、JPA、JDO、Hibernate等和實際使用的事務管理器之間的區別,其沒有必要一一對應,在使用JDBC時可使用JTA全局事務管理器,一樣,使用Hibernate也能夠,只需將最後一步的事務管理器替換爲JtaTransactionManager便可。
事務管理器到特定方法配置

    接下來就是具體事務管理器如何做用在具體方法上的配置了,不過在這以前咱們描述一下這個使用背景,如前所述,Spring提供了兩種事務管理方式,一種是聲明式事務管理(Declarative transaction management),另外一種則是編程式事務管理(Programmatic transaction management),這兩種方式顯然聲明式比較簡單,而在事務管理需求比較少的時候,想比較聲明式比較繁瑣的配置,編程式則比較簡單,這裏你們根據本身的需求進行選擇,Spring官方推薦使用聲明式事務管理,後面的論述也會分別給出聲明式事務和編程式事務相關主題。
聲明式配置


    聲明式事務管理的基本原理是利用在Spring中應用比較普遍的面向切面編程(AOP),經過將事務處理放在切面對象中來進行一致性的管理,減小冗餘代碼,提高簡潔性,原理圖大體以下 sql


其中的AOP proxy,Spring定義了TransactionInterceptor類,該類結構以下 數據庫

經過這個結構,咱們看到實際的方法都在TransactionInterceptor的父類TransactionAspectSupport中,TransactionInterceptor的關鍵方法是invoke方法,而這個方法內部調用了父類的invokeWithinTransaction方法,這是這裏事務處理的核心,invokeWithinTransaction方法片斷以下

這看起來是一個典型的切面代理方法,咱們按照順序講下主要流程,首先270行獲取該方法的事務屬性,這些屬性包括事務隔離級別、事務傳播策略、只讀、回滾等屬性信息,而後271行獲取平臺事務管理器,由於Spring支持多事務管理器特性,這裏須要配合實際配置來決定具體的事務管理器,注意276行,這一行獲取了事務信息,同時又在必要狀況下開啓了事務,後面281行是具體方法執行,291行提交事務。
    上面說了些原理,這裏給出聲明式事務的一些具體應用配置,在Spring中聲明式事務大致有兩種用法,一種是純粹的切面配置,一種是基於@Transactional的註解配置,這裏都會給出具體應用實例。
  1.基於切面的配置
    基於界面的配置比較簡單,核心是Transaction 的Advice的配置,而後將這Advice配置在具體pointcut上就能夠了,咱們先看看Advice的配置,配置實例以下:
<tx:advice id="txAdvice" transaction-manager="txManager">
    <!-- the transactional semantics... -->
    <tx:attributes>
            <!-- all methods starting with 'get' are read-only -->
        <tx:method name="get*" read-only="true"/>
          <!-- other methods use the default transaction settings (see below) -->
        <tx:method name="*"/>
    </tx:attributes>
</tx:advice>

如上,這裏的配置比較清晰明瞭,引用具體的事務管理器(若是事務管理器名稱爲transactionManager,那麼能夠不須要明確引用事務管理器),而後通配符匹配特定方法,配置特定事務屬性;配置完Advice,接下來是將Advice關聯到pointcut的配置,以下
<aop:config>
    <aop:pointcut id="fooServiceOperation" expression="execution(* x.y.service.FooService.*(..))"/>
    <aop:advisor advice-ref="txAdvice" pointcut-ref="fooServiceOperation"/>
</aop:config>
這裏的配置比較簡單,不在贅述

關於事務屬性的配置,可配置屬性以下

屬性 express

是否必須 apache

默認值 編程

描述 設計模式

name 性能優化

服務器


方法名,可適用通配符(* session

propagation

REQUIRED

事務傳播策略

isolation

DEFAULT

事務隔離級別

timeout

-1(無限制)

事務超時時間

read-only

false

是不是隻讀事務

rollback-for


事務回滾的異常

no-rollback-for


事務不回滾的異常

除方法名以外,其餘屬性都是可選的,根據具體的應用場景進行定製,事務傳播策略定義了不一樣事務的存在關係,可選屬性列表以下(注意Spring事務概念中邏輯事務和物理事務的區別,物理事務就是指數據庫層面的事務,而邏輯事務是應用層面的,能夠具備更豐富的行爲特性,Spirng中的事務就是特指邏輯事務,要注意)

傳播策略

描述

REQUIRED

支持當前上下文中存在事務,若是沒有事務就建立一個新的,若是有,則在當前事務下執行

SUPPORTS

支持當前上下文中存在事務,若是沒有事務,就在無事務上下文下執行

MANDATORY

支持當前上下文中存在事務,若是沒有事務就拋出異常

REQUIRES_NEW

產生一個新的事務,若是當前上下文中有事務就掛起原來事務

NOT_SUPPORTED

不支持當前上下文存在事務,老是在無事務上下文下執行,存在的事務會進行掛起操做

NEVER

不支持當前上下文存在事務,若是存在事務則拋出異常

NESTED

若是當前上下文中存在事務就執行一個內嵌的事務

事務隔離級別這個屬性是數據庫層面的,可選屬性列表以下

傳播策略

避免問題

不能避免問題

READ_UNCOMMITTED

第一更新丟失

髒讀、不可重複讀、幻讀

READ_COMMITTED

第一更新丟失、髒讀

不可重複讀、幻讀

REPEATABLE_READ

第一更新丟失、髒讀、不可重複讀

幻讀

SERIALIZABLE

第一更新丟失、髒讀、不可重複讀、幻讀


具體這些問題以及每一個隔離級別的具體特徵有機會在進行敘述吧,否則太多了,這裏注意不一樣的數據庫對不一樣隔離級別支持是不一樣的,例如Oracle只能支持讀提交和序列化,不可重複讀的問題經過額外的樂觀鎖實現,其中存在只讀事務,隔離級別實際是序列化,在配置事務只讀時要注意這裏的特性;Mysql支持比較豐富,但其可重複讀可以解決部分幻讀問題,這是與其實現有關的,等有機會把它展開敘述,通常應用場景下設置讀提交就能知足要求,讀未提交隔離太低,而其餘兩個隔離級別又過重量級,使用的話會嚴重下降應用性能,對於一些併發問題的容忍性,第一更新丟失、髒讀、不可重複讀(特殊場景爲第二更新丟失)是不能容忍的,這裏可使用讀提交隔離級別+樂觀鎖來屏蔽第二更新丟失的問題,這是一個權衡場景須要和性能狀況下作出的綜合性方案。
事務超時設置能夠根據須要設置,通常狀況下,爲了防止數據庫鎖阻塞,全部方法都應該有事務超時時間。
只讀事務設置注意其性能優化的價值,通常狀況下,只讀操做可能都不須要事務,這時候下降事務隔離級別或者乾脆不要事務都可以提高應用性能。
事務異常回滾,關於受檢異常和非受檢異常的爭論這裏就不在論述了,Spring事務採用了JTA的慣例,默認狀況下只用運行時異常及其子類會致使事務回滾,受檢異常不會致使事務回滾,後面兩個選項就是關於回滾與不回滾異常設置的,這裏能夠根據須要進行設置。

2.基於@Transactional的事務管理
說完了切面設置的事務管理,這裏說一下基於@Transactional的事務管理,咱們看下該註解的一些定義,以下
經過上面的定義,咱們知道這個註解能夠放在類上和特定方法(只能爲public方法,protected、private、package-visible都不能夠)上,放在類上的話,該類全部的public方法都有了事務,放在特定方法上能夠定製這個方法執行的事務屬性,爲了啓用這種註解式的事務管理方式,須要在xml中添加以下信息
<tx:annotation-driven transaction-manager="txManager"/>
這時候就能夠把以前事務配置中切面的部分刪掉了,關於該註解上的屬性,由於和上面切面形式中相似,這裏就不在贅述了

上面的配置仍是基於XML的,這裏給出一個徹底基於註解的實例

註解配置類以下

這裏開啓bean掃描,開啓基於@Transactional的事務管理

其中的一些Bean定義,DataSource

SessionFactory

TransactionManager

在初始化的部分使用AnnotationConfigApplicationContext初始化就能夠了

編程式事務管理

    說完了聲明式的事務管理,接下來說講編程式事務管理,Spring提供了兩種編程方式,一種是使用TransactionTemplate,一種是直接使用具體PlatformTransactionManager,官方推薦第一種方式,接下來咱們分別進行敘述。
1. 使用TransactionTemplate
TransactionTemplate採用一種回調的方式將要執行方法包含在事務中,其初始化須要指定特定的事務管理器,實際使用代碼片斷以下
transactionTemplate.execute(new TransactionCallback() {
    // the code in this method executes in a transactional context
    public Object doInTransaction(TransactionStatus status) {
        updateOperation1();
        return resultOfUpdateOperation2();
    }
})
上例是有返回結果的,能夠不須要返回結果,以下
transactionTemplate.execute(new TransactionCallbackWithoutResult() {
    protected void doInTransactionWithoutResult(TransactionStatus status) {
        updateOperation1();
        updateOperation2();
    }
})
能夠在方法體內回滾事務,設置status.setRollbackOnly()便可
TransactionTemplate提供了一些事務屬性設置接口,能夠根據須要進行設置。
2.使用PlatformTransactionManager
這個比較簡單,關鍵是定義TransactionDefinition,代碼以下
DefaultTransactionDefinition def = new DefaultTransactionDefinition();
// explicitly setting the transaction name is something that can only be done programmatically
def.setName("SomeTxName");
def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
TransactionStatus status = txManager.getTransaction(def);
try {
    // execute your business logic here
}
catch (MyException ex) {
    txManager.rollback(status);
    throw ex;
}
txManager.commit(status);

代碼很清晰明瞭,這裏就不在贅述了。

寫不下去了,算是作了一次梳理吧,有時間在進行詳細敘述。
相關文章
相關標籤/搜索