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>