通常通過TransactionProxyFactoryBean設(shè)置Spring事務(wù)代理。我們需要一個目標(biāo)對象包裝在事務(wù)代理中。這個目標(biāo)對象一般是一個普通Java對象的bean。當(dāng)我們定義TransactionProxyFactoryBean時,必須提供一個相關(guān)的 PlatformTransactionManager的引用和
事務(wù)屬性。
事務(wù)屬性含有上面描述的事務(wù)定義。
<bean id="petStore"
class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean">
<property name="transactionManager"><ref bean="transactionManager"/></property>
<property name="target"><ref bean="petStoreTarget"/></property>
<property name="transactionAttributes">
<props>
<prop key="insert*">PROPAGATION_REQUIRED,-MyCheckedException</prop>
<prop key="update*">PROPAGATION_REQUIRED</prop>
<prop key="*">PROPAGATION_REQUIRED,readOnly</prop>
</props>
</property>
</bean>
事務(wù)代理會實現(xiàn)目標(biāo)對象的接口:這里是id為petStoreTarget的bean。(使用 CGLIB也可以實現(xiàn)具體類的代理。只要設(shè)置proxyTargetClass屬性為true就可以。如果目標(biāo)對象沒有實現(xiàn)任何接口,這將自動設(shè)置該屬性為true。通常,我們希望面向接口而不是類編程。)使用proxyInterfaces屬性來限定事務(wù)代理來代 理指定接口也是可以的
(一般來說是個好想法)。也可以通過從 org.springframework.aop.framework.ProxyConfig繼承或所有AOP代理工廠共享 的屬性來定制TransactionProxyFactoryBean的行為。
這里的transactionAttributes屬性定義在 org.springframework.transaction.interceptor.NameMatchTransactionAttributeSource 中的屬性格式來設(shè)置。這個包括通配符的方法名稱映射是很直觀的。注意 insert*的映射的值包括回滾規(guī)則。添加的-MyCheckedException 指定如果方法拋出MyCheckedException或它的子類,事務(wù)將 會自動回滾。可以用逗號分隔定義多個回滾規(guī)則。-前綴強制回滾,+前綴指定提交(這允許即使拋出unchecked異常時也可以提交事務(wù),當(dāng)然你自己要明白自己 在做什么)。
TransactionProxyFactoryBean允許你通過 “preInterceptors”和“postInterceptors”屬性設(shè)置“前”或“后”通知來提供額外的 攔截行為。可以設(shè)置任意數(shù)量的“前”和“后”通知,它們的類型可以是 Advisor(可以包含一個切入點), MethodInterceptor或被當(dāng)前Spring配置支持的通知類型 (例如ThrowAdvice, AfterReturningtAdvice或BeforeAdvice, 這些都是默認(rèn)支持的)。這些通知必須支持實例共享模式。如果你需要高級AOP特 性來使用事務(wù),如有狀態(tài)的maxin,那最好使用通用的 org.springframework.aop.framework.ProxyFactoryBean, 而不是TransactionProxyFactoryBean實用代理創(chuàng)建者。
也可以設(shè)置自動代理:配置AOP框架,不需要單獨的代理定義類就可以生成類的 代理。
附兩個spring的事務(wù)配置例子:
<prop key="add">
PROPAGATION_REQUIRES_NEW, -MyException
</prop>
注:上面的意思是add方法將獨占一個事務(wù),當(dāng)事務(wù)處理過程中產(chǎn)生MyException異常或者該異常的子類將回滾該事務(wù)。
<prop key="loadAll">
PROPAGATION_SUPPORTS, ISOLATION_READ_COMMITED, Readonly
</prop>
注:表示loadAll方法支持事務(wù),而且不會讀取沒有提交事務(wù)的數(shù)據(jù)。它的數(shù)據(jù)為只讀(這樣有助于提高讀取的性能)
附A Spring中的所有事務(wù)策略
PROPAGATION_MANDATORY
PROPAGATION_NESTED
PROPAGATION_NEVER
PROPAGATION_NOT_SUPPORTED
PROPAGATION_REQUIRED
PROPAGATION_REQUIRED_NEW
PROPAGATION_SUPPORTS
附B Spring中所有的隔離策略:
ISOLATION_DEFAULT
ISOLATION_READ_UNCOMMITED
ISOLATION_COMMITED
ISOLATION_REPEATABLE_READ
ISOLATION_SERIALIZABLE
Spring事務(wù)類型祥解
大家可能在spring中經(jīng)常看到這樣的定義:
<prop key="load*">PROPAGATION_REQUIRED,readOnly</prop><prop key="store*">PROPAGATION_REQUIRED</prop>
估計有好多朋友還沒有弄清楚里面的值的意思,仔細(xì)看完下面應(yīng)該知道自己什么情況下面應(yīng)該使用什么樣的聲明。^_^
Spring中常用事務(wù)類型:
- PROPAGATION_REQUIRED--支持當(dāng)前事務(wù),如果當(dāng)前沒有事務(wù),就新建一個事務(wù)。這是最常見的選擇。
- PROPAGATION_SUPPORTS--支持當(dāng)前事務(wù),如果當(dāng)前沒有事務(wù),就以非事務(wù)方式執(zhí)行。
- PROPAGATION_MANDATORY--支持當(dāng)前事務(wù),如果當(dāng)前沒有事務(wù),就拋出異常。
- PROPAGATION_REQUIRES_NEW--新建事務(wù),如果當(dāng)前存在事務(wù),把當(dāng)前事務(wù)掛起。
- PROPAGATION_NOT_SUPPORTED--以非事務(wù)方式執(zhí)行操作,如果當(dāng)前存在事務(wù),就把當(dāng)前事務(wù)掛起。
- PROPAGATION_NEVER--以非事務(wù)方式執(zhí)行,如果當(dāng)前存在事務(wù),則拋出異常。
- PROPAGATION_NESTED--如果當(dāng)前存在事務(wù),則在嵌套事務(wù)內(nèi)執(zhí)行。如果當(dāng)前沒有事務(wù),則進行與PROPAGATION_REQUIRED類似的操作。
摘要:
Spring和EJB一樣,提供了兩種事務(wù)管理方式:編程式和聲明式。在考試系統(tǒng)中我們將使用聲明式的事務(wù)管理,這是spring推薦的做法。使用這種方式可以體驗到spring的強大便捷,而且我們無須在Dao類中編寫任何特殊的代碼,只需要通過配置文件就可以讓普通的java類加載到事務(wù)管理中,這個意義是很重大的。
本文Matrix永久鏡像:
http://www.matrix.org.cn/resource/article/1/1339.html
說明:本文可能由Matrix原創(chuàng),也可能由Matrix的會員整理,或者由
Matrix的Crawler在全球知名Java或者其他技術(shù)相關(guān)站點抓取并永久
保留鏡像,Matrix會保留所有原來的出處URL,并在顯著地方作出說明,
如果你發(fā)覺出處URL有誤,請聯(lián)系Matrix改正.
四、Spring中的事務(wù)控制
Spring和EJB一樣,提供了兩種事務(wù)管理方式:編程式和聲明式。在考試系統(tǒng)中我們將使用聲明式的事務(wù)管理,這是spring推薦的做法。使用這種方式可以體驗到spring的強大便捷,而且我們無須在Dao類中編寫任何特殊的代碼,只需要通過配置文件就可以讓普通的java類加載到事務(wù)管理中,這個意義是很重大的。
Spring中進行事務(wù)管理的通常方式是利用AOP(面向切片編程)的方式,為普通java類封裝事務(wù)控制,它是通過動態(tài)代理實現(xiàn)的,由于接口是延遲實例化的,spring在這段時間內(nèi)通過攔截器,加載事務(wù)切片。原理就是這樣,具體細(xì)節(jié)請參考jdk中有關(guān)動態(tài)代理的文檔。本文主要講解如何在spring中進行事務(wù)控制。
動態(tài)代理的一個重要特征是,它是針對接口的,所以我們的dao要通過動態(tài)代理來讓spring接管事務(wù),就必須在dao前面抽象出一個接口,當(dāng)然如果沒有這樣的接口,那么spring會使用CGLIB來解決問題,但這不是spring推薦的方式,我們也不做討論。
參照前面的例子,我們?yōu)镾tudentManager.java定義一個接口,它的內(nèi)容如下:
/*
* 創(chuàng)建日期 2005-3-25
*/
package org.bromon.spring.examer.student;
import java.util.List;
import org.bromon.spring.examer.pojo.Student;
/**
* @author Bromon
*/
public interface StudentManagerInterface
{
public void add(Student s);
public void del(Student s);
public void update(Student s);
public List loadAll();
public Student loadById(int id);
}
StudentManager也應(yīng)該做出修改,實現(xiàn)該接口:
public class StudentManager extends HibernateDaoSupport implements StudentManagerInterface
現(xiàn)在需要修改配置文件,用于定義Hibrenate適用的事務(wù)管理器,并且把sessionFactory注入進去,同時還需要通過注冊一個DefaultTransactionAttribute對象,來指出事務(wù)策略。其中sessionFactory的定義已經(jīng)在本文的第三章中說明。
首先定義一個Hibernate的事務(wù)管理器,讓它來管理sessionFactory:
<bean id="transactionManager" class="org.springframework.orm.hibernate.HibernateTransactionManager">
<property name="sessionFactory">
<ref bean="sessionFactory"/>
</property>
</bean>
下面定義事務(wù)管理策略,我們希望把策略定義在方法這個級別上,提供最大的靈活性,本例中將add方法定義為:PROPAGATION_REQUIRES_NEW,這可以保證它將始終運行在一個事務(wù)中。
<bean id="transactionAttributeSource" class="org.springframework.transaction.interceptor.NameMatchTransactionAttributeSource">
<property name="properties">
<props>
<prop key="add">
PROPAGATION_REQUIRES_NEW
</prop>
</props>
</property>
</bean>
我們不僅可以為add方法定義事務(wù)策略,還可以定義事務(wù)隔離程度和回滾策略,他們以逗號隔開,比如我們的add事務(wù)可以定義為:
<prop key="add">
PROPAGATION_REQUIRES_NEW,-ExamerException
</prop
>
這個事務(wù)策略表示add方法將會獨占一個事務(wù),當(dāng)事務(wù)過程中產(chǎn)生ExamerException異常,事務(wù)會回滾。
Add/update/del都是寫入方法,對于select(讀取)方法,我們可以指定較為復(fù)雜的事務(wù)策略,比如對于loadAll()方法:
<prop key=”loadAll”>
PROPAGATION_SUPPORTS,ISOLATION_READ_COMMITED,readOnly
</prop>
該事務(wù)的含義為,loadAll方法支持事務(wù),不會讀去位提交的數(shù)據(jù),它的數(shù)據(jù)為只讀(可提高執(zhí)行速度)。
如你所見,我們的StudentManagerInterface接口中還有一個loadById(int id)方法,也許我們將來還會有很多的loadByXXXX的方法,難道要意義為他們指定事務(wù)策略?太煩人了,他們應(yīng)該和loadAll()一樣,所以我們可以使用通配符,定義所有的loadXXXX方法:
<prop key=”load*”>
PROPAGATION_SUPPORTS,ISOLATION_READ_COMMITED,readOnly
</prop>
現(xiàn)在可以定義事務(wù)管理器:
<bean id="studentManager" class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean">
<property name="target">
<ref bean="studentManager"/>
</property>
<property name="transactionManager">
<ref bean="transactionManager"/>
</property>
<property name="transactionAttributeSource">
<ref bean="transactionAttributeSource"/>
</property>
</bean>
這個bean的外觀是一個接口(StudentManagerInterface),我們指出了它的具體實現(xiàn)(studentManager),而且為它綁定了事務(wù)策略。在客戶端使用的時候,獲得對象是StudentManagerInterface,所有的操作都是針對這個接口的。測試代碼并沒有改變,我們雖然修改了很多地方,加入了事務(wù)控制,但是客戶端并沒有受到影響,這也體現(xiàn)了spring的一些優(yōu)勢。測試代碼如下:
public void testAdd()
{
ApplicationContext ctx=new ClassPathXmlApplicationContext("springConfig.xml");
StudentManager sm=(StudentManager)ctx.getBean("studentManager");
Student s=new Student();
s.setId(1);
s.setName("bromon");
s.setPassword("123");
s.setGrade(1);
s.setSex(0);
sm.add(s);
}
通過以上的代碼可以看出,spring可以簡單的把普通的java class納入事務(wù)管理,聲明性的事務(wù)操作起來也很容易。有了spring之后,聲明性事務(wù)不再是EJB獨有,我們不必為了獲得聲明性事務(wù)的功能而去忍受EJB帶來的種種不便。
我所使用的mysql是不支持事務(wù)的,你可以更換使用PostgreSQL,有了spring+hibernate,更換db并不像以前那樣恐怖了,步驟很簡單:
1、 添加PostgreSQL的jdbc驅(qū)動
2、 修改dataSource配置,包括驅(qū)動名稱、url、帳號、密碼
3、 修改sessionFactory的數(shù)據(jù)庫dailet為net.sf.hibernate.dialect.PostgreSQLDialect
4、 修改hbm.xml中的主鍵生成策略為increment
所有的修改都在配置文件中完成,業(yè)務(wù)代碼不需要任何修改,我很滿意,How about u?
傳統(tǒng)的:
1 <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
2 <property name="driverClassName" value="oracle.jdbc.driver.OracleDriver" />
3 <property name="url" value="jdbc:oracle:thin:@127.0.0.1:1521:dev" />
4 <property name="username" value="kaktos" />
5 <property name="password" value="kaktos" />
6 </bean>
7
8 <bean id="txManager"
9 class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
10 <property name="dataSource" ref="dataSource" />
11 </bean>
12
13 <bean id="businessBean"
14 class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean">
15 <property name="transactionManager" ref="txManager" />
16 <property name="target" ref="businessBeanTarget" />
17 <property name="transactionAttributes">
18 <props>
19 <prop key="*">PROPAGATION_REQUIRED</prop>
20 </props>
21 </property>
22 </bean>
23
24 <bean id="businessBeanTarget" class="sample.spring.trans.BusinessBean">
25 <property name="dataSource" ref="dataSource" />
26 </bean>
這樣做的弊端就是不得不為每個需要事務(wù)的bean做一次聲明,如果所有的bean都基本上有一致的配置,這樣就太繁瑣啦。
下面是第二種方式:
1 <beans>
2 <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
3 <property name="driverClassName" value="oracle.jdbc.driver.OracleDriver" />
4 <property name="url" value="jdbc:oracle:thin:@127.0.0.1:1521:dev" />
5 <property name="username" value="kaktos" />
6 <property name="password" value="kaktos" />
7 </bean>
8
9 <bean id="txManager"
10 class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
11 <property name="dataSource" ref="dataSource" />
12 </bean>
13
14 <bean id="matchAllWithPropReq"
15 class="org.springframework.transaction.interceptor.MatchAlwaysTransactionAttributeSource">
16 <property name="transactionAttribute" value="PROPAGATION_REQUIRED" />
17 </bean>
18
19 <bean id="matchAllTxInterceptor" class="org.springframework.transaction.interceptor.TransactionInterceptor">
20 <property name="transactionManager" ref="txManager" />
21 <property name="transactionAttributeSource" ref="matchAllWithPropReq" />
22 </bean>
23
24 <bean id="autoProxyCreator"
25 class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator">
26 <property name="interceptorNames">
27 <list>
28 <idref local="matchAllTxInterceptor" />
29 </list>
30 </property>
31 <property name="beanNames">
32 <list>
33 <idref local="businessBean" />
34 </list>
35 </property>
36 </bean>
37
38 <!-- my beans -->
39 <bean id="businessBean" class="sample.spring.trans.BusinessBean">
40 <property name="dataSource" ref="dataSource" />
41 </bean>
42 </beans>
BeanNameAutoProxyCreator會在applicationcontext初始化后自動為beanNames屬性中的bean建立proxy。