本文研究的主要是spring的事務機制的相關內容,具體如下。
java ee傳統事務機制
通常有兩種事務策略:全局事務和局部事務。全局事務可以跨多個事務性資源(即數據源,典型的是數據庫和消息隊列),通常都需要j2ee應用服務器的管理,其底層需要服務器的jta支持。而局部事務則與底層采用的持久化技術有關,如果底層直接使用jdbc,需要用connection對象來操事務。如果采用hibernate持久化技術,則需要使用session對象來操作事務。
通常的,使用jta事務,jdbc事務及hibernate事務的編程流程大致如下,
上圖也可以看出,采用傳統事務編程,程序代碼必須和具體的事務策略的api耦合,如果應用需要切換一種策略,意味著需要大幅修改代碼。但是如果使用spring事務的話,就不會有這個問題了。
spring事務機制
sring沒有提供任何事務支持,它只是負責包裝底層的事務,而在spring層面,對外提供統一的編程api。spring事務的核心是platformtransactionmanager接口,
platformtransactionmanager代表與具體類型無關的事務接口,可以代表任何事務,包括jdbc事務,hibernate事務,甚至是jta事務。
springa事務機制是一種典型的策略模式,platformtransactionmanager代表事務管理接口,但它并不知道到底如何管理事務,它只要求事務管理提供開始事務gettransaction(),
提交事務commit()
和回滾事務rollback()
這三個方法,但具體如何實現則交給其實現類完成。編程人員只需要在配置文件中根據具體需要使用的事務類型做配置,spring底層就自動會使用具體的事務實現類進行事務操作,而對于程序員來說,完全不需要關心底層過程,只需要面向platformtransactionmanager接口進行編程即可。platformtransactionmanager接口中提供了如下方法:gettransaction(..), commit(); rollback();
這些都是與平臺無關的事務操作。
gettransaction()
的完整寫法為 transactionstatus gettransaction(transactiondefinition definiton)
這個方法用來返回一個事務對象,其中的參數transactiondefinition 則可以為事務對象指定各種屬性,通常可以指定 事務的隔離屬性, 傳播屬性, 超時,只讀 這幾個屬性。
spring具體的事務管理需要在配置文件中配置好platformtransactionmanager,下面是不同類型的事務對應的spring配置。
jdbc數據源的局部事務管理器的配置如下,
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
<!-- 定義數據源bean,使用c3p0數據源實現,并注入數據源的必要信息 --> <bean id= "datasource" class = "com.mchange.v2.c3p0.combopooleddatasrouce" destroy-method= "close" p:driverclass= "com.mysql.jdbc.driver" p:jdbcurl= "jdbc:mysql://localhost/test" p:user= "root" p:password= "" p:maxpoolsize= "40" p:minpoolsize= "2" p:initialpoolsize= "2" p:maxidletime= "30" /> <!-- 配置jdbc數據源的局部數據管理器,使用datasourcetransactionmanager類 --> <bean id= "transactionmanager" class = "org.springframework.jdbc.datasource.datasourcetransactionmanager" p:datasource-ref= "datasource" /> |
容器管理的jta全局事務管理器的配置如下,
1
2
3
4
5
|
<bean id= "datasource" class = "org.springframework.jndi.jndiobjectfactorybean" p:jndiname= "jdbc/jpetstore" /> <!-- 使用jtatransactionmanager類, 該類實現了platformtransactionmanager接口 --> <!-- 使用jta全局事務,spring容器可以自行從java ee服務器中獲取事務性資源,無需依賴注入 --> <bean id= "transactionmanager" class = "org.springframework.transaction.jta.jtatransactionmanager" /> |
對于jta全局事務,只需要指定事務管理器的實現類jtatransactionmanager即可,spring容器會自行從j2ee服務器獲取數據源,無需顯式注入進事務管理器。
基于hibernate持久化技術的spring局部事務配置如下,
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
|
<!-- 定義數據源bean,使用c3p0數據源實現,并注入數據源的必要信息 --> <bean id= "datasource" class = "com.mchange.v2.c3p0.combopooleddatasrouce" destroy-method= "close" p:driverclass= "com.mysql.jdbc.driver" p:jdbcurl= "jdbc:mysql://localhost/test" p:user= "root" p:password= "" p:maxpoolsize= "40" p:minpoolsize= "2" p:initialpoolsize= "2" p:maxidletime= "30" /> <!-- 定義hibernate的sessionfactory, sessionfactory需要依賴數據源,注入datasource --> <bean id= "sessionfactory" class = "org.springframework.orm.hibernate4.localsessionfactorybean" p:datasource-ref= "datasource" > <!-- annotatedclasses用來列出全部持久化類 --> <property name= "annotatedclasses" > <list> <!-- 以下用來列出所有po類 --> <value>com.entity.user</value> </list> </property> <!-- 定義hibernate的sessionfactory屬性 --> <property name= "hibernateproperties" > <props> <!-- 指定hibernate的連接方言 --> <prop key= "hibernate.dialect" >org.hibernate.dialect.mysql5innodbdialect</prop> <!-- 是否根據hibernate映射表創建數據表 --> <prop key= "hibernate.hbm2ddl.auto" >update</prop> </props> </property> </bean> <!-- 配置hibernate的局部數據管理器,使用hibernatetransactionmanager類 --> <!-- 該類是platformtransactionmanager接口針對hibernate的特定實現 --> <!-- 配置hibernatetransactionmanager需要注入sessionfactory --> <bean id= "transactionmanager" class = "org.springframework.orm.hibernate4.hibernatetransactionmanager" p:sessionfactory-ref= "sessionfactory" /> |
spring事務如果采用hibernate策略,一般需要配置三點:數據源, sessionfactory, 事務管理器。
如果底層采用hibernate持久層技術,而事務采用jta全局事務時,配置如下,
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
|
<!-- 配置jta數據源--> <bean id= "datasource" class = "org.springframework.jndi.jndiobjectfactorybean" p:jndiname= "jdbc/jpetstore" /> <!-- 定義hibernate的sessionfactory, sessionfactory需要依賴數據源,注入datasource --> <bean id= "sessionfactory" class = "org.springframework.orm.hibernate4.localsessionfactorybean" p:datasource-ref= "datasource" > <!-- annotatedclasses用來列出全部持久化類 --> <property name= "annotatedclasses" > <list> <!-- 以下用來列出所有po類 --> <value>com.entity.user</value> </list> </property> <!-- 定義hibernate的sessionfactory屬性 --> <property name= "hibernateproperties" > <props> <!-- 指定hibernate的連接方言 --> <prop key= "hibernate.dialect" >org.hibernate.dialect.mysql5innodbdialect</prop> <!-- 是否根據hibernate映射表創建數據表 --> <prop key= "hibernate.hbm2ddl.auto" >update</prop> </props> </property> </bean> <!-- 使用jtatransactionmanager類,該類是platformtransactionmanager接口的實現類 --> <!-- 針對全局事務管理的特定實現 --> <bean id= "transactionmanager" class = "org.springframework.transaction.jta.jtatransactionmanager" /> |
這與前面的基于hibernate的spring事務比起來,就是將數據源換成了jndi數據源, 將事務管理器換成了jtatransactionmanager.
對于jta全局事務,因為需要底層應用服務器的支持,而不同應用服務器所提供的jta全局事務可能存在細節上的差異,因此實際配置全局事務管理器時可能需要使用jtatransactionmanager的子類,例如oracle的javaee應用服務器提供的oc4jjtatransactionmanager,oracle為weblogic提供的weblogicjtatransactionmanager, ibm為websphere提供的websphereuowtransactionmanager等。
從上面各種事務類型的spring配置可以看出,當應用程序采用spring事務管理時,應用程序無需與具體的事務api耦合,應用程序只需要面向platormtransactionmanager接口編程即可,applicationcontext會根據配置文件選擇合適的事務策略實現類(即platormtransactionmanager的實現類)。
那么在具體在spring中如何進行事務控制編程呢,通常有兩種方式,
編程式事務管理:就是直接在代碼中使用platormtransactionmanager提供的三個抽象方法進行事務流程控制。也可以在spring容器中獲取platormtransactionmanager類型的bean,該bean總是platormtransactionmanager的具體實現類的實例,具體的實現類則由applicationcontext按照策略模式進行選擇,編程人員無需關心,只需要面向接口編程即可。
聲明式事務管理:這種方式不需要講事務控制流程寫入代碼中,而是通過aop的方式,完全由配置文件完成事務的織入。即xml配置文件可以為業務組件配置事務代理,事務代理為業務組件提供事務控制。現在這種方式是最好的,源碼侵入性最低。
使用聲明式事務管理-使用xml schema配置事務策略
當使用聲明式事務時,只需要寫好配置文件,配置需要事務控制的組件種類,業務組件就會在aop機制下被織入事務控制,而編程人員不需要寫任何事務管理代碼,可以專注于業務組件的開發。因此通常都推薦使用聲明式事務管理。
spring的xml schema方式提供了簡潔的事務配置策略,通過命名空間 <tx:advice>
來配置一個事務增強處理,其中可以指定事務的各種屬性(例如隔離屬性, 傳播屬性, 超時,只讀屬性等等),然后通過<aop:config>標簽可以將事務的增強與aop的切入點(即bean的執行方法)進行綁定,從而實現對bean的方法織入事務操作。下面是一個簡單的例子,配置一個newsdaoimpl bean進行數據操作,使用c3p0數據源,spring的jdbc事務管理器,在<tx:advice對事務設置屬性。
完整的spring配置如下,
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
|
<?xml version= "1.0" encoding= "utf-8" ?> <beans xmlns:xsi= "http://www.w3.org/2001/xmlschema-instance" xmlns= "http://www.springframework.org/schema/beans" xmlns:p= "http://www.springframework.org/schema/p" xmlns:aop= "http://www.springframework.org/schema/aop" xmlns:tx= "http://www.springframework.org/schema/tx" xsi:schemalocation="http: //www.springframework.org/schema/beans http: //www.springframework.org/schema/beans/spring-beans-4.0.xsd http: //www.springframework.org/schema/aop http: //www.springframework.org/schema/aop/spring-aop-4.0.xsd http: //www.springframework.org/schema/tx http: //www.springframework.org/schema/tx/spring-tx-4.0.xsd"> <!-- 定義數據源bean,使用c3p0數據源實現,并注入數據源的必要信息 --> <bean id= "datasource" class = "com.mchange.v2.c3p0.combopooleddatasource" destroy-method= "close" p:driverclass= "com.mysql.jdbc.driver" p:jdbcurl= "jdbc:mysql://localhost/test?useunicode=true&characterencoding=utf-8" p:user= "root" p:password= "" p:maxpoolsize= "40" p:minpoolsize= "2" p:initialpoolsize= "2" p:maxidletime= "30" /> <!-- 配置jdbc數據源的局部數據管理器,使用datasourcetransactionmanager類 --> <bean id= "transactionmanager" class = "org.springframework.jdbc.datasource.datasourcetransactionmanager" p:datasource-ref= "datasource" /> <!-- 配置一個業務邏輯bean --> <bean id= "newsdao" class = "com.dao.impl.newsdaoimpl" p:ds-ref= "datasource" /> <!-- 配置事務增強處理, 指定事務管理器 --> <tx:advice id= "txadvice" transaction-manager= "transactionmanager" > <!-- 用于配置詳細的事務定義 --> <tx:attributes> <!-- 所有以get開頭的方法都是只讀的 --> <tx:method name= "get*" read-only= "true" /> <!-- 其他方法默認都適用事務,指定超時 5 秒 --> <tx:method name= "*" isolation= "default" propagation= "required" timeout= "5" /> </tx:attributes> </tx:advice> <aop:config> <!-- 配置一個切入點,匹配impl包下所有以impl結尾的類里的所有方法的執行 --> <aop:pointcut expression= "execution(* com.dao.impl.*impl.*(..))" id= "mypointcut" /> <!-- 將切入點mypointcut和增強txadvice綁定--> <aop:advisor advice-ref= "txadvice" pointcut-ref= "mypointcut" /> <!-- 再配置一個切入點,匹配impl包下所有以abc開頭類里的所有方法的執行 --> </aop:config> </beans> |
newsdaoimpl代碼中,則是插入重復數據到表中,
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
package com.dao.impl; import javax.sql.datasource; import org.springframework.jdbc.core.jdbctemplate; import com.dao.newsdao; public class newsdaoimpl implements newsdao { private datasource ds; public void setds(datasource ds) { this .ds = ds; } @override public void insert(string title, string content) { //c3p0數據池的用法 jdbctemplate jt = new jdbctemplate(ds); jt.update( "insert into news_inf" + " values(100,?,?)" , title, content); jt.update( "insert into news_inf" + " values(100,?,?)" , title, content); //如果沒有事務控制,則第一條記錄可以被插入 //如果增加事務控制,將發現第一條記錄也插不進去 } } |
下面是測試方法,
1
2
3
4
5
6
7
|
public static void test3() { applicationcontext ctx = new classpathxmlapplicationcontext( "beans4jdbc.xml" ); //獲取事務代理bean newsdao dao = (newsdao)ctx.getbean( "newsdao" , newsdao. class ); dao.insert( "java編程核心思想" , "輕量級java ee開發" ); system.out.println( "執行完畢" ); } |
執行測試方法會發現拋出異常(因為有重復數據),而又因為事務控制,數據庫中講不會有數據插入。
可以看到上面例子中,通常對于xml schema的配置中,其實就是對一個普通的bean做了aop配置,織入一個advice增強,而advice增強中則配置一個事務管理器,事務管理器又依賴數據源。
對于<aop:advisor>中,將advice和切入點的綁定,而在spring底層是由bean后處理器完成(例如beannameautoproxycreator, defaultadvisorautoproxycreator),其本質就是動態代理。
另外,在<tx:advice>配置增強中,還可以為事務指定再遇到特定異常時,進行強制rollback和強制不rollback,即rollback-for="xxxexception", no-rollback-for="xxxexception"
使用@transactionl
除了使用xml schema的方法之外,也可以直接在方法上添加@transaction注解,使這個方法具有事務屬性。 在@transaction中可以為事務配置各種屬性(例如隔離屬性, 傳播屬性, 超時,只讀屬性等等),此外,還需要在在xml配置中加入<tx:annotation-triven配置表明spring會根據注解來配置事務代理,這樣,事務的屬性配置和aop切入配置就可以只通過一步(直接通過注解配置在方法名上)完成了。
1
|
<tx:annotation-driven transaction-manager= "transactionmanager" /> |
newsdaoimpl.
1
2
3
|
@transactional (propagation=propagation.required, isolation=isolation. default , timeout= 5 ) @override public void insert(string title, string content) { |
總結
以上就是本文關于spring的事務機制實例代碼的全部內容,希望對大家有所幫助。感興趣的朋友可以繼續參閱本站其他相關專題,如有不足之處,歡迎留言指出。感謝朋友們對本站的支持!
原文鏈接:http://www.cnblogs.com/fysola/p/6384399.html