spring詳解(十一)「事務(wù)」

5.Spring的事務(wù)

通常情況下碗降,J2EE有2種事務(wù)管理方式:全局事務(wù)本地事務(wù)隘竭,2種事務(wù)都比較明顯的缺陷。

全局事務(wù)
全局事務(wù)允許跨多個(gè)事務(wù)資源的事務(wù)管理(通常是關(guān)系數(shù)據(jù)庫(kù)和消息隊(duì)列)讼渊,應(yīng)用服務(wù)器通過(guò)JTA(一個(gè)很復(fù)雜的api)管理全局事務(wù)动看,此外,一個(gè)JTA的事務(wù)通常通過(guò)JNDI進(jìn)行資源查找爪幻,即如果你想使用JTA就必須連帶使用JNDI菱皆。JTA通常只能在應(yīng)用服務(wù)器環(huán)境下使用,顯然使用全局事務(wù)會(huì)限制應(yīng)用代碼的重用性挨稿。

更好的方式是通過(guò)EJB CMT提供全局事務(wù)管理仇轻,CMT是一種聲明式的事務(wù)管理。EJB CMT移除了事務(wù)相關(guān)的JNDI查找奶甘,雖然使用EJB本身就需要使用JNDI篷店,但這確實(shí)節(jié)省了大量的事務(wù)管理代碼(并不是全部),重大缺陷是CMT綁定在JTA以及應(yīng)用服務(wù)器環(huán)境上臭家,并且你必須選擇EJB來(lái)處理業(yè)務(wù)邏輯疲陕。EJB太龐大,并不是一個(gè)很吸引人的選擇(僅僅是為了增加全局事務(wù)就使用EJB钉赁,確實(shí)....)蹄殃。

本地事務(wù)
本地事務(wù)則和底層所使用的持久化技術(shù)有關(guān)(使用JDBC處理持久化,事務(wù)管理需要JDBC中的connection對(duì)象橄霉,使用hibernate處理持久化窃爷,事務(wù)管理需要hibernate中的session對(duì)象)邑蒋,比起全局事務(wù),本地事務(wù)更易使用按厘,但是也有明顯的缺陷:1.不能跨事務(wù)資源医吊,例如:使用JDBC事務(wù)來(lái)進(jìn)行事務(wù)管理的代碼在JTA全局事務(wù)環(huán)境下就不能運(yùn)行。2.使用本地事務(wù)逮京,由于應(yīng)用服務(wù)器不需要參與事務(wù)的管理卿堂,因此不能保證跨多個(gè)事務(wù)性資源的事務(wù)正確性(不需要特別注意這種情況,大多數(shù)應(yīng)用使用單個(gè)事務(wù)資源)懒棉。3.顯然事務(wù)管理和代碼是耦合的草描,具有侵入性(這也是前面缺陷1的產(chǎn)生原因)。

Spring事務(wù)管理

Spring解決了全局事務(wù)本地事務(wù)的缺陷策严,允許應(yīng)用開(kāi)發(fā)者在任何環(huán)境下使用一致的編程模型穗慕。Spring同時(shí)支持編程式事務(wù)管理聲明式事務(wù)管理。當(dāng)使用Spring編程式事務(wù)管理時(shí)妻导,開(kāi)發(fā)者直接使用Spring框架的事務(wù)抽象(事務(wù)抽象這個(gè)翻譯有點(diǎn)拗口逛绵,原文為:transaction abstraction,不同具體事務(wù)策略的統(tǒng)一抽象接口倔韭,面向接口編程)术浪,使應(yīng)用程序可以運(yùn)行在任何具體的底層事務(wù)基礎(chǔ)之上。當(dāng)使用Spring聲明式事務(wù)管理時(shí)寿酌,只需編寫(xiě)少量和事務(wù)相關(guān)的代碼即可(只需編寫(xiě)一些Spring配置文件)胰苏,這些代碼和Spring的事務(wù)api或任何其他的事務(wù)api都沒(méi)有耦合。顯然Spring的聲明式事務(wù)管理更加簡(jiǎn)單易用醇疼。

為了使用事務(wù)管理是否應(yīng)該選擇應(yīng)用服務(wù)器硕并?
答案是否定的。
Spring對(duì)事務(wù)管理的支持改變了企業(yè)級(jí)Java應(yīng)用必須要使用應(yīng)用服務(wù)器的傳統(tǒng)觀念僵腺。需要特別指出的是鲤孵,僅僅是為了使用EJB的聲明式事務(wù)管理就選擇使用應(yīng)用服務(wù)器是完全沒(méi)有必要的(為了使用聲明式事務(wù)管理就使用EJB同樣可怕),盡管應(yīng)用服務(wù)器提供了強(qiáng)勁的JTA功能辰如,Spring的聲明式事務(wù)管理比起EJB CMT更加給力普监,并且提供更加高效的編程方式。通常選擇使用應(yīng)用服務(wù)器是為了跨多個(gè)事務(wù)資源的事務(wù)管理琉兜,這種情況很少見(jiàn)并且許多高端應(yīng)用會(huì)選擇使用高性能數(shù)據(jù)庫(kù)而不是多個(gè)事務(wù)資源凯正。也可以選擇一些獨(dú)立的事務(wù)管理技術(shù),但是大多數(shù)情況下這些技術(shù)都需要一些應(yīng)用服務(wù)器功能(比如JMS或是JCA)的支持豌蟋。Spring更符合一次編寫(xiě)廊散,隨處運(yùn)行的原則,代價(jià)僅僅是修改一些Spring配置文件梧疲,而不是大量的重復(fù)性編碼工作允睹。

Spring支持的事務(wù)策略

Spring事務(wù)策略是通過(guò)PlatformTransactionManager接口體現(xiàn)的运准,該接口是Spring事務(wù)策略的核心
即使使用容器管理的JTA缭受,代碼依然不需要JNDI查找胁澳,無(wú)需與特定的JTA資源耦合在一起,通過(guò)配置文件米者,JTA資源傳給PlatformTransactionManager的實(shí)現(xiàn)類(lèi)韭畸,因此,程序的代碼可以在JTA事務(wù)管理和非JTA事務(wù)管理之間輕松的切換蔓搞。

Spring是否支持事務(wù)跨多個(gè)數(shù)據(jù)庫(kù)資源胰丁?

答:Spring完全支持這種跨多個(gè)事務(wù)性資源的全局事務(wù),前提是底層的應(yīng)用服務(wù)器(如WebLogic喂分、WebSphere锦庸、tomcat等)支持JTA全局事務(wù),可以這樣說(shuō):Spring本身沒(méi)有任何事務(wù)支持妻顶,它只是負(fù)責(zé)包裝底層的事務(wù)————當(dāng)我們?cè)诔绦蛑忻嫦騊latformTransactionManager接口編程時(shí)酸员,Spring在底層負(fù)責(zé)將這些操作轉(zhuǎn)換成具體的事務(wù)操作代碼,所以應(yīng)用底層支持什么樣的事務(wù)策略讳嘱,那么Spring就支持什么樣的事務(wù)策略。Spring事務(wù)管理的優(yōu)勢(shì)是將應(yīng)用從具體的事務(wù)API中分離出來(lái)酿愧,而不是真正提供事務(wù)管理的底層實(shí)現(xiàn)沥潭。

Spring的具體的事務(wù)管理由PlatformTransactionManager的不同實(shí)現(xiàn)類(lèi)來(lái)完成,在Spring容器中配置PlatformTransactionManager時(shí)必須針對(duì)不同的環(huán)境提供不同的實(shí)現(xiàn)類(lèi)嬉挡,下面提供了不同的持久化訪問(wèn)環(huán)境钝鸽,及其對(duì)應(yīng)的PlatformTransactionManager實(shí)現(xiàn)類(lèi)的配置。
1庞钢,BC數(shù)據(jù)源的局部事務(wù)策略的配置文件如下:

<!-- 定義數(shù)據(jù)源Bean拔恰,使用C3P0數(shù)據(jù)源實(shí)現(xiàn) -->
    <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"
        destroy-method="close">
        <!-- 指定連接數(shù)據(jù)庫(kù)的驅(qū)動(dòng) -->
        <property name="driverClass" value="com.mysql.jdbc.Driver"/>
        <!-- 指定連接數(shù)據(jù)庫(kù)的URL -->
        <property name="jdbcUrl" value="jdbc:mysql://localhost/javaee"/>
        <!-- 指定連接數(shù)據(jù)庫(kù)的用戶(hù)名 -->
        <property name="user" value="root"/>
        <!-- 指定連接數(shù)據(jù)庫(kù)的密碼 -->
        <property name="password" value="32147"/>
        <!-- 指定連接數(shù)據(jù)庫(kù)連接池的最大連接數(shù) -->
        <property name="maxPoolSize" value="40"/>
        <!-- 指定連接數(shù)據(jù)庫(kù)連接池的最小連接數(shù) -->
        <property name="minPoolSize" value="1"/>
        <!-- 指定連接數(shù)據(jù)庫(kù)連接池的初始化連接數(shù) -->
        <property name="initialPoolSize" value="1"/>
        <!-- 指定連接數(shù)據(jù)庫(kù)連接池的連接的最大空閑時(shí)間 -->
        <property name="maxIdleTime" value="20"/>
    </bean>
    <!-- 配置JDBC數(shù)據(jù)源的局部事務(wù)管理器,使用DataSourceTransactionManager 類(lèi) -->
    <!-- 該類(lèi)實(shí)現(xiàn)PlatformTransactionManager接口基括,是針對(duì)采用數(shù)據(jù)源連接的特定實(shí)現(xiàn)-->
    <bean id="transactionManager" 
class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <!-- 配置DataSourceTransactionManager時(shí)需要依注入DataSource的引用 -->
        <property name="dataSource" ref="dataSource"/>
    </bean>

2.器管理JTA全局事務(wù)的配置文件如下:

<!-- 配置JNDI數(shù)據(jù)源Bean-->
<bean id="dateSource" class="org.springframework.jndi.JndiObjectFactoryBean">
    <!--容器管理數(shù)據(jù)源的JNDI-->
<property name="jndiName" value="jdbc/jpetstore"/>
</bean>
<!--使用JtaTransactionManager類(lèi)颜懊,該類(lèi)實(shí)現(xiàn)PlatformTransactionManager接口-->
<!--針對(duì)采用全局事務(wù)管理的特定實(shí)現(xiàn)-->
<bean id="transactionManager" class="org.springframework.transaction.jta.JtaTransactionManager"/>

3.采用Hibernate持久層訪問(wèn)策略時(shí),局部事務(wù)的策略的配置文件如下:

!-- 定義數(shù)據(jù)源Bean风皿,使用C3P0數(shù)據(jù)源實(shí)現(xiàn) -->
    <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"
        destroy-method="close">
        <!-- 指定連接數(shù)據(jù)庫(kù)的驅(qū)動(dòng) -->
        <property name="driverClass" value="com.mysql.jdbc.Driver"/>
        <!-- 指定連接數(shù)據(jù)庫(kù)的URL -->
        <property name="jdbcUrl" value="jdbc:mysql://localhost/javaee"/>
        <!-- 指定連接數(shù)據(jù)庫(kù)的用戶(hù)名 -->
        <property name="user" value="root"/>
        <!-- 指定連接數(shù)據(jù)庫(kù)的密碼 -->
        <property name="password" value="32147"/>
        <!-- 指定連接數(shù)據(jù)庫(kù)連接池的最大連接數(shù) -->
        <property name="maxPoolSize" value="40"/>
        <!-- 指定連接數(shù)據(jù)庫(kù)連接池的最小連接數(shù) -->
        <property name="minPoolSize" value="1"/>
        <!-- 指定連接數(shù)據(jù)庫(kù)連接池的初始化連接數(shù) -->
        <property name="initialPoolSize" value="1"/>
        <!-- 指定連接數(shù)據(jù)庫(kù)連接池的連接的最大空閑時(shí)間 -->
        <property name="maxIdleTime" value="20"/>
    </bean>
    <!-- 定義Hibernate的SessionFactory -->
    <bean id="sessionFactory"
        class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
        <!-- 依賴(lài)注入數(shù)據(jù)源河爹,注入正是上面定義的dataSource -->
        <property name="dataSource" ref="dataSource"/>
        <!-- mappingResouces屬性用來(lái)列出全部映射文件 -->
        <property name="mappingResources">
            <list>
                <!-- 以下用來(lái)列出Hibernate映射文件 -->
                <value>Person.hbm.xml</value>
            </list>
        </property>
        <!-- 定義Hibernate的SessionFactory的屬性 -->
        <property name="hibernateProperties">
            <props>
                <!-- 指定數(shù)據(jù)庫(kù)方言 -->
                <prop key="hibernate.dialect">
                    org.hibernate.dialect.MySQLInnoDBDialect</prop>
                <!-- 是否根據(jù)需要每次自動(dòng)創(chuàng)建數(shù)據(jù)庫(kù) -->
                <prop key="hibernate.hbm2ddl.auto">update</prop>
                <!-- 顯示Hibernate持久化操作所生成的SQL -->
                <prop key="hibernate.show_sql">true</prop>
                <!-- 將SQL腳本進(jìn)行格式化后再輸出 -->
                <prop key="hibernate.format_sql">true</prop>
            </props>
        </property>
    </bean>
<!--配置Hibernate局部事務(wù)管理器,使用HibernateTransactionManager類(lèi)-->
<!--該類(lèi)是PlatformTransactionManager接口針對(duì)采用Hibernate的特定實(shí)現(xiàn)類(lèi)-->
<bean id="transactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager">
    <property name="sessionFactory" ref="sessionFactory"/>
</bean>

4.底層采用Hibernate持久化技術(shù)桐款,但事務(wù)依然采用JTA全局事務(wù):

<!-- 定義數(shù)據(jù)源Bean咸这,使用C3P0數(shù)據(jù)源實(shí)現(xiàn) -->
    <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"
        destroy-method="close">
        <!-- 指定連接數(shù)據(jù)庫(kù)的驅(qū)動(dòng) -->
        <property name="driverClass" value="com.mysql.jdbc.Driver"/>
        <!-- 指定連接數(shù)據(jù)庫(kù)的URL -->
        <property name="jdbcUrl" value="jdbc:mysql://localhost/javaee"/>
        <!-- 指定連接數(shù)據(jù)庫(kù)的用戶(hù)名 -->
        <property name="user" value="root"/>
        <!-- 指定連接數(shù)據(jù)庫(kù)的密碼 -->
        <property name="password" value="32147"/>
        <!-- 指定連接數(shù)據(jù)庫(kù)連接池的最大連接數(shù) -->
        <property name="maxPoolSize" value="40"/>
        <!-- 指定連接數(shù)據(jù)庫(kù)連接池的最小連接數(shù) -->
        <property name="minPoolSize" value="1"/>
        <!-- 指定連接數(shù)據(jù)庫(kù)連接池的初始化連接數(shù) -->
        <property name="initialPoolSize" value="1"/>
        <!-- 指定連接數(shù)據(jù)庫(kù)連接池的連接的最大空閑時(shí)間 -->
        <property name="maxIdleTime" value="20"/>
    </bean>
 <!-- 定義Hibernate的SessionFactory -->
    <bean id="sessionFactory"
        class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
        <!-- 依賴(lài)注入數(shù)據(jù)源,注入正是上面定義的dataSource -->
        <property name="dataSource" ref="dataSource"/>
        <!-- mappingResouces屬性用來(lái)列出全部映射文件 -->
        <property name="mappingResources">
            <list>
                <!-- 以下用來(lái)列出Hibernate映射文件 -->
                <value>Person.hbm.xml</value>
            </list>
        </property>
        <!-- 定義Hibernate的SessionFactory的屬性 -->
        <property name="hibernateProperties">
            <props>
                <!-- 指定數(shù)據(jù)庫(kù)方言 -->
                <prop key="hibernate.dialect">
                    org.hibernate.dialect.MySQLInnoDBDialect</prop>
                <!-- 是否根據(jù)需要每次自動(dòng)創(chuàng)建數(shù)據(jù)庫(kù) -->
                <prop key="hibernate.hbm2ddl.auto">update</prop>
                <!-- 顯示Hibernate持久化操作所生成的SQL -->
                <prop key="hibernate.show_sql">true</prop>
                <!-- 將SQL腳本進(jìn)行格式化后再輸出 -->
                <prop key="hibernate.format_sql">true</prop>
            </props>
        </property>
    </bean>
<!--使用JtaTransactionManager類(lèi)魔眨,該類(lèi)實(shí)現(xiàn)PlatformTransactionManager接口-->
<!--針對(duì)采用全局事務(wù)管理的特定實(shí)現(xiàn)-->
<bean id="transactionManager" class="org.springframework.transaction.jta.JtaTransactionManager"/>

從上面的配置文件可以看出媳维,當(dāng)使用Spring事務(wù)管理事務(wù)策略時(shí)酿雪,應(yīng)用程序無(wú)需與具體的事務(wù)策略耦合,Spring提供了兩種事務(wù)管理方式:
編程式管理方式:即使使用Spring編程式事務(wù)時(shí)侄刽,程序也可直接獲取容器中的TransactionManagerBean指黎,該Bean總是PlatformTransactionManager的實(shí)例,可以通過(guò)該接口提供的3個(gè)方法來(lái)開(kāi)始事務(wù)唠梨,提交事務(wù)和回滾事務(wù)袋励。

聲明式管理方式:無(wú)需在Java程序中書(shū)寫(xiě)任何的事務(wù)操作代碼,而是通過(guò)在XML文件中為業(yè)務(wù)組件配置事務(wù)代理(AOP代理的一種)当叭,AOP事務(wù)代理所織入的增強(qiáng)處理也是由Spring提供:在目標(biāo)方法前茬故,織入開(kāi)始事務(wù);在目標(biāo)方法后執(zhí)行蚁鳖,織入結(jié)束事務(wù)磺芭。

注意:Spring編程事務(wù)還可以通過(guò)TransactionTemplate類(lèi)來(lái)完成,該類(lèi)提供了一個(gè)execute方法醉箕,可以更簡(jiǎn)潔的方式來(lái)進(jìn)行事務(wù)操作钾腺。

當(dāng)使用聲明式事務(wù)時(shí),開(kāi)發(fā)者無(wú)需書(shū)寫(xiě)任何事務(wù)管理代碼讥裤,不依賴(lài)Spring或或任何事務(wù)API放棒,Spring的聲明式事務(wù)編程無(wú)需任何額外的容器支持,Spring容器本身管理聲明式事務(wù)己英,使用聲明式事務(wù)策略间螟,可以讓開(kāi)發(fā)者更好的專(zhuān)注于業(yè)務(wù)邏輯的實(shí)現(xiàn)。

使用TransactionProxyFactoryBean創(chuàng)建事務(wù)代理

在Spring1.X损肛,聲明事務(wù)使用TransactionProxyFactoryBean來(lái)配置事務(wù)代理Bean厢破,正如他的名字所暗示的,它是一個(gè)工廠Bean治拿,該工程Bean專(zhuān)為目標(biāo)Bean生成事務(wù)代理Bean摩泪,既然TransactionProxyFactoryBean產(chǎn)生的是事務(wù)代理Bean,可見(jiàn)Spring的聲明式事務(wù)策略是基于Spring AOP的劫谅。
例如:

<beans>
    <!-- 定義數(shù)據(jù)源Bean见坑,使用C3P0數(shù)據(jù)源實(shí)現(xiàn) -->
    <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"
        destroy-method="close">
        <!-- 指定連接數(shù)據(jù)庫(kù)的驅(qū)動(dòng) -->
        <property name="driverClass" value="com.mysql.jdbc.Driver"/>
        <!-- 指定連接數(shù)據(jù)庫(kù)的URL -->
        <property name="jdbcUrl" value="jdbc:mysql://localhost/javaee"/>
        <!-- 指定連接數(shù)據(jù)庫(kù)的用戶(hù)名 -->
        <property name="user" value="root"/>
        <!-- 指定連接數(shù)據(jù)庫(kù)的密碼 -->
        <property name="password" value="32147"/>
        <!-- 指定連接數(shù)據(jù)庫(kù)連接池的最大連接數(shù) -->
        <property name="maxPoolSize" value="40"/>
        <!-- 指定連接數(shù)據(jù)庫(kù)連接池的最小連接數(shù) -->
        <property name="minPoolSize" value="1"/>
        <!-- 指定連接數(shù)據(jù)庫(kù)連接池的初始化連接數(shù) -->
        <property name="initialPoolSize" value="1"/>
        <!-- 指定連接數(shù)據(jù)庫(kù)連接池的連接的最大空閑時(shí)間 -->
        <property name="maxIdleTime" value="20"/>
    </bean>
    <!-- 配置JDBC數(shù)據(jù)源的局部事務(wù)管理器,使用DataSourceTransactionManager 類(lèi) -->
    <!-- 該類(lèi)實(shí)現(xiàn)PlatformTransactionManager接口同波,是針對(duì)采用數(shù)據(jù)源連接的特定實(shí)現(xiàn)-->
    <bean id="transactionManager" 
        class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <!-- 配置DataSourceTransactionManager時(shí)需要依注入DataSource的引用 -->
        <property name="dataSource" ref="dataSource"/>
    </bean>
    <!-- 配置一個(gè)業(yè)務(wù)邏輯Bean -->
    <bean id="test" class="lee.TestImpl">
        <property name="ds" ref="dataSource"/>
    </bean>
    <!-- 為業(yè)務(wù)邏輯Bean配置事務(wù)代理 -->
    <bean id="testTrans" class=
        "org.springframework.transaction.interceptor.TransactionProxyFactoryBean">
        <!-- 為事務(wù)代理工廠Bean注入事務(wù)管理器 -->
        <property name="transactionManager" ref="transactionManager"/> 
        <property name="target" ref="test"/>
        <!-- 指定事務(wù)屬性 -->
        <property name="transactionAttributes"> 
            <props> 
                <prop key="*">PROPAGATION_REQUIRED</prop> 
            </props>
        </property>
    </bean> 
</beans>
public class TestImpl implements Test
{
    private DataSource ds;
    public void setDs(DataSource ds)
    {
        this.ds = ds;
    }
    public void insert(String u)
    {
        JdbcTemplate jt = new JdbcTemplate(ds);
        jt.execute("insert into mytable values('" + u + "')");
        //兩次插入相同的數(shù)據(jù)鳄梅,將違反主鍵約束
        jt.execute("insert into mytable values('" + u + "')");
        //如果增加事務(wù)控制,我們發(fā)現(xiàn)第一條記錄也插不進(jìn)去未檩。
        //如果沒(méi)有事務(wù)控制戴尸,則第一條記錄可以被插入
    }
}

配置事務(wù)代理的時(shí)。需要傳入一個(gè)事務(wù)管理器冤狡,一個(gè)目標(biāo)Bena孙蒙,并指定該事務(wù)的事務(wù)屬性项棠,事務(wù)實(shí)行由transactionAttribute指定,上面只有一條事務(wù)傳播規(guī)則挎峦,該規(guī)則指定對(duì)于所有方法都使用PROPAGATION_REQUIRED的傳播屬性香追,Spring支持的事務(wù)傳播規(guī)則:

PROPAGATION_MANDATORY:要求調(diào)用該方法的線程已處于事務(wù)環(huán)境中,否則拋出異常坦胶;
PROPAGATION_NESTED:如果執(zhí)行該方法的線程已處于事務(wù)的環(huán)境下透典,依然啟動(dòng)新的事務(wù),方法在嵌套的事務(wù)里執(zhí)行顿苇,如果執(zhí)行該方法的線程并未處于事務(wù)的環(huán)境下峭咒,也啟動(dòng)新的事務(wù),此時(shí)與PROPAGATION_REQUIRED纪岁;
PROPAGATION_NOT_SUPPORTED:如果調(diào)用該方法的線程處于事務(wù)中凑队,則暫停事務(wù),再執(zhí)行方法幔翰。
PROPAGATION_NEVER:不允許執(zhí)行該方法的線程處于事務(wù)的環(huán)境下漩氨,如果執(zhí)行該方法的線程處于事務(wù)的環(huán)境下,則會(huì)拋出異常遗增;
PROPAGATION_REQUIRED:要求在事務(wù)環(huán)境中執(zhí)行該方法叫惊,如果當(dāng)前執(zhí)行線程已處于事務(wù)中,則直接調(diào)用做修;否則赋访,則啟動(dòng)新的事務(wù)后執(zhí)行該方法。
PROPAGATION_REQUIREDS_NEW:該方法要求在新的事務(wù)中執(zhí)行缓待,如果當(dāng)前執(zhí)行線程已處于事務(wù)環(huán)境下,則暫停當(dāng)前事務(wù)渠牲,啟動(dòng)新事務(wù)后執(zhí)行方法旋炒;否則,啟動(dòng)新的事務(wù)后執(zhí)行方法签杈。
PROPAGATION_SUPPORTS:如果當(dāng)前線程處于事務(wù)中瘫镇,則使用當(dāng)前事務(wù),否則不使用事務(wù)答姥。

主程序代碼:
public class MainTest
{
    public static void main(String[] args) 
    {
        //創(chuàng)建Spring容器
        ApplicationContext ctx = new 
            ClassPathXmlApplicationContext("bean.xml");
        //獲取事務(wù)代理Bean
        Test t = (Test)ctx.getBean("testTrans");----------①
        //執(zhí)行插入操作
        t.insert("bbb");
    }
}

上面①處代碼獲取testTrans Bean铣除,該Bean已不再是TestImpl的實(shí)例了,它只是Spring容器創(chuàng)建的事務(wù)代理鹦付,該事務(wù)代理以TestImpl實(shí)例為目標(biāo)對(duì)象尚粘,且該目標(biāo)對(duì)象也實(shí)現(xiàn)了Test接口,故代理對(duì)象也可當(dāng)作Test實(shí)例使用敲长。
實(shí)際上郎嫁,Spring不僅支持對(duì)接口的代理秉继,整合GBLIB后,Spring甚至可以對(duì)具體類(lèi)生成代理泽铛,只要設(shè)置proxyTargetClass屬性為true就可以了尚辑,如果目標(biāo)Bean沒(méi)有實(shí)現(xiàn)任何接口,proxyTargetClass默認(rèn)為true盔腔,此時(shí)Spring會(huì)對(duì)具體的類(lèi)生成代理杠茬。當(dāng)然通常建議面向接口編程,而不面向具體的實(shí)現(xiàn)類(lèi)編程弛随。

Spring2.X的事務(wù)配置策略

上面介紹的TransactionProxyFactoryBean配置策略簡(jiǎn)單易懂瓢喉,但是配置起來(lái)極為繁瑣:每個(gè)目標(biāo)Bean都需要額外配置一個(gè)TransactionProxyFactoryBean代理,這種方法將導(dǎo)致配置文件的急劇增加撵幽。
Spring2.X的XML Schema方式提供了更簡(jiǎn)潔事務(wù)配置策略灯荧,Spring提供了tx命名空間來(lái)配置事務(wù)管理,tx命名空間下提供了<tx:advice../>元素來(lái)配置事務(wù)切面盐杂,一旦使用了該元素配置了切面逗载,就可以直接使用<aop:advisor.../>元素啟動(dòng)自動(dòng)代理。

如:

<!-- 定義數(shù)據(jù)源Bean链烈,使用C3P0數(shù)據(jù)源實(shí)現(xiàn) -->
    <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"
        destroy-method="close">
        <!-- 指定連接數(shù)據(jù)庫(kù)的驅(qū)動(dòng) -->
        <property name="driverClass" value="com.mysql.jdbc.Driver"/>
        <!-- 指定連接數(shù)據(jù)庫(kù)的URL -->
        <property name="jdbcUrl" value="jdbc:mysql://localhost/javaee"/>
        <!-- 指定連接數(shù)據(jù)庫(kù)的用戶(hù)名 -->
        <property name="user" value="root"/>
        <!-- 指定連接數(shù)據(jù)庫(kù)的密碼 -->
        <property name="password" value="32147"/>
        <!-- 指定連接數(shù)據(jù)庫(kù)連接池的最大連接數(shù) -->
        <property name="maxPoolSize" value="40"/>
        <!-- 指定連接數(shù)據(jù)庫(kù)連接池的最小連接數(shù) -->
        <property name="minPoolSize" value="1"/>
        <!-- 指定連接數(shù)據(jù)庫(kù)連接池的初始化連接數(shù) -->
        <property name="initialPoolSize" value="1"/>
        <!-- 指定連接數(shù)據(jù)庫(kù)連接池的連接的最大空閑時(shí)間 -->
        <property name="maxIdleTime" value="20"/>
    </bean>
    <!-- 配置JDBC數(shù)據(jù)源的局部事務(wù)管理器厉斟,使用DataSourceTransactionManager 類(lèi) -->
    <!-- 該類(lèi)實(shí)現(xiàn)PlatformTransactionManager接口,是針對(duì)采用數(shù)據(jù)源連接的特定實(shí)現(xiàn)-->
    <bean id="transactionManager" 
        class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <!-- 配置DataSourceTransactionManager時(shí)需要依注入DataSource的引用 -->
        <property name="dataSource" ref="dataSource"/>
    </bean>
    <!-- 配置一個(gè)業(yè)務(wù)邏輯Bean -->
    <bean id="test" class="lee.TestImpl">
        <property name="ds" ref="dataSource"/>
    </bean>
    <!-- 配置事務(wù)切面Bean,指定事務(wù)管理器 -->
    <tx:advice id="txAdvice" transaction-manager="transactionManager">
        <!-- 用于配置詳細(xì)的事務(wù)語(yǔ)義 -->
        <tx:attributes>
            <!-- 所有以'get'開(kāi)頭的方法是read-only的 -->
            <tx:method name="get*" read-only="true"/>
            <!-- 其他方法使用默認(rèn)的事務(wù)設(shè)置 -->
            <tx:method name="*"/>
        </tx:attributes>
    </tx:advice>
    <aop:config>
        <!-- 配置一個(gè)切入點(diǎn)强衡,匹配lee包下所有以Impl結(jié)尾的類(lèi)
            執(zhí)行的所有方法 -->
        <aop:pointcut id="leeService"
            expression="execution(* lee.*Impl.*(..))"/>
        <!-- 指定在txAdvice切入點(diǎn)應(yīng)用txAdvice事務(wù)切面 -->
        <aop:advisor advice-ref="txAdvice" 
            pointcut-ref="leeService"/>
    </aop:config>
</beans>

提示:Advisor的作用:將Advice和切入點(diǎn)綁在一起擦秽,保證Advice所包含的增強(qiáng)處理在對(duì)應(yīng)的切入點(diǎn)被織入。

主程序:

public class MainTest
{
    public static void main(String[] args) 
    {
        //創(chuàng)建Spring容器
        ApplicationContext ctx = new 
            ClassPathXmlApplicationContext("bean.xml");
        //獲取事務(wù)代理Bean
        Test t = (Test)ctx.getBean("test");
        //執(zhí)行插入操作
        t.insert("bbb");
    }
}

上面的配置文件直接獲取容器中的test Bean漩勤,因?yàn)镾pring AOP會(huì)為該Bean自動(dòng)織入事務(wù)增強(qiáng)處理的方式感挥,所以test Bean的所有方法都具有事務(wù)性。
配置<tx:advice../>元素時(shí)除了需要一個(gè)transaction-Manager之外越败,重要的就是需要配置一個(gè)<attributes.../>子元素触幼,該元素又可以包含多個(gè)<method../>子元素。

可以看出配置<tx:advice../>元素重點(diǎn)就是配置<method.../>子元素究飞,每個(gè)<method../>子元素為一批方法指定所需的事務(wù)語(yǔ)義置谦。

配置<method../>元素時(shí)可以指定如下屬性:

name:必選屬性,與該事務(wù)語(yǔ)義關(guān)聯(lián)的方法名亿傅,該屬性支持通配符媒峡。
propagation:指定事務(wù)傳播行為,該屬性可以為Propagation枚舉類(lèi)的任意一個(gè)枚舉值葵擎,該屬性 的默認(rèn)值是Propagation.REQUIRED谅阿;

isolation:指定事務(wù)隔離級(jí)別,該屬性值可為Isolation枚舉類(lèi)的任意枚舉值。
timeout:指定事務(wù)超時(shí)時(shí)間(以秒為單位)奔穿,指定-1意味不超時(shí)镜沽,默認(rèn)值是-1;
read-only:指定事務(wù)是否只讀贱田,該屬性默認(rèn)為false缅茉;
rollback-for:指定觸發(fā)事務(wù)回滾的異常類(lèi)(全名),該屬性可以指定多個(gè)異常類(lèi),用英文逗號(hào)隔開(kāi)男摧;
no-rollback-for:指定不觸發(fā)事務(wù)回滾的類(lèi)蔬墩,該屬性可以指定多個(gè)異常類(lèi),并且用英文的逗號(hào)隔開(kāi)耗拓。
因此我們可以為不同的業(yè)務(wù)邏輯方法指定不同的事務(wù)策略拇颅,如:

<tx:advice id="aaa">
    <tx:attributes>
        <tx:method name="get*" read--only="true"/>
         <tx:method name="*"/>
    </tx:attributes>
</tx:advice>
<tx:advice id="bbb">
    <tx:attributes>
                <tx:method name="*" propagation="NEVER"/>
    </tx:attributes>
</tx:advice>
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市乔询,隨后出現(xiàn)的幾起案子樟插,更是在濱河造成了極大的恐慌,老刑警劉巖竿刁,帶你破解...
    沈念sama閱讀 222,104評(píng)論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件黄锤,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡食拜,警方通過(guò)查閱死者的電腦和手機(jī)鸵熟,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,816評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)负甸,“玉大人流强,你說(shuō)我怎么就攤上這事∩氪” “怎么了打月?”我有些...
    開(kāi)封第一講書(shū)人閱讀 168,697評(píng)論 0 360
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)蚕捉。 經(jīng)常有香客問(wèn)我僵控,道長(zhǎng),這世上最難降的妖魔是什么鱼冀? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 59,836評(píng)論 1 298
  • 正文 為了忘掉前任,我火速辦了婚禮悠就,結(jié)果婚禮上千绪,老公的妹妹穿的比我還像新娘。我一直安慰自己梗脾,他們只是感情好荸型,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,851評(píng)論 6 397
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著炸茧,像睡著了一般瑞妇。 火紅的嫁衣襯著肌膚如雪稿静。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 52,441評(píng)論 1 310
  • 那天辕狰,我揣著相機(jī)與錄音改备,去河邊找鬼。 笑死蔓倍,一個(gè)胖子當(dāng)著我的面吹牛悬钳,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播偶翅,決...
    沈念sama閱讀 40,992評(píng)論 3 421
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼默勾,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了聚谁?” 一聲冷哼從身側(cè)響起母剥,我...
    開(kāi)封第一講書(shū)人閱讀 39,899評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎形导,沒(méi)想到半個(gè)月后环疼,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,457評(píng)論 1 318
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡骤宣,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,529評(píng)論 3 341
  • 正文 我和宋清朗相戀三年秦爆,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片憔披。...
    茶點(diǎn)故事閱讀 40,664評(píng)論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡等限,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出芬膝,到底是詐尸還是另有隱情望门,我是刑警寧澤,帶...
    沈念sama閱讀 36,346評(píng)論 5 350
  • 正文 年R本政府宣布锰霜,位于F島的核電站筹误,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏癣缅。R本人自食惡果不足惜厨剪,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,025評(píng)論 3 334
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望友存。 院中可真熱鬧祷膳,春花似錦、人聲如沸屡立。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 32,511評(píng)論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至勇皇,卻和暖如春罩句,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背敛摘。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,611評(píng)論 1 272
  • 我被黑心中介騙來(lái)泰國(guó)打工门烂, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人着撩。 一個(gè)月前我還...
    沈念sama閱讀 49,081評(píng)論 3 377
  • 正文 我出身青樓诅福,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親拖叙。 傳聞我的和親對(duì)象是個(gè)殘疾皇子氓润,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,675評(píng)論 2 359

推薦閱讀更多精彩內(nèi)容

  • Spring Boot 參考指南 介紹 轉(zhuǎn)載自:https://www.gitbook.com/book/qbgb...
    毛宇鵬閱讀 46,859評(píng)論 6 342
  • Spring Cloud為開(kāi)發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見(jiàn)模式的工具(例如配置管理,服務(wù)發(fā)現(xiàn)薯鳍,斷路器咖气,智...
    卡卡羅2017閱讀 134,704評(píng)論 18 139
  • application的配置屬性。 這些屬性是否生效取決于對(duì)應(yīng)的組件是否聲明為Spring應(yīng)用程序上下文里的Bea...
    新簽名閱讀 5,381評(píng)論 1 27
  • 這些屬性是否生效取決于對(duì)應(yīng)的組件是否聲明為 Spring 應(yīng)用程序上下文里的 Bean(基本是自動(dòng)配置的)挖滤,為一個(gè)...
    發(fā)光的魚(yú)閱讀 1,432評(píng)論 0 14
  • 如題崩溪,isKindOfClass 與 isMemberOfClass 的區(qū)別創(chuàng)建了兩個(gè)類(lèi) teacher 和per...
    joymake閱讀 187評(píng)論 0 0