Spring的事務機制

JAVA EE傳統事務機制

一般有兩種事務策略:全局事務和局部事務。全局事務能夠跨多個事務性資源(即數據源,典型的是數據庫和消息隊列),一般都須要J2EE應用服務器的管理,其底層須要服務器的JTA支持。而局部事務則與底層採用的持久化技術有關,若是底層直接使用JDBC,須要用Connection對象來操事務。若是採用Hibernate持久化技術,則須要使用session對象來操做事務。html

一般的,使用JTA事務,JDBC事務及Hibernate事務的編程流程大體以下,java

 

上圖也能夠看出,採用傳統事務編程,程序代碼必須和具體的事務策略的API耦合,若是應用須要切換一種策略,意味着須要大幅修改代碼。可是若是使用Spring事務的話,就不會有這個問題了。mysql

Spring事務機制

Sring沒有提供任何事務支持,它只是負責包裝底層的事務,而在Spring層面,對外提供統一的編程API。Spring事務的核心是PlatformTransactionManager接口,程序員

PlatformTransactionManager表明與具體類型無關的事務接口,能夠表明任何事務,包括JDBC事務,Hibernate事務,甚至是JTA事務。spring

Springa事務機制是一種典型的策略模式,PlatformTransactionManager表明事務管理接口,但它並不知道到底如何管理事務,它只要求事務管理提供開始事務getTransaction(),提交事務commit()和回滾事務rollback()這三個方法,但具體如何實現則交給其實現類完成。編程人員只須要在配置文件中根據具體須要使用的事務類型作配置,Spring底層就自動會使用具體的事務實現類進行事務操做,而對於程序員來講,徹底不須要關心底層過程,只須要面向PlatformTransactionManager接口進行編程便可。PlatformTransactionManager接口中提供了以下方法:getTransaction(..), commit(); rollback(); 這些都是與平臺無關的事務操做。sql

getTransaction()的完整寫法爲 TransactionStatus getTransaction(TransactionDefinition definiton)數據庫

這個方法用來返回一個事務對象,其中的參數TransactionDefinition 則能夠爲事務對象指定各類屬性,一般能夠指定 事務的隔離屬性, 傳播屬性, 超時,只讀 這幾個屬性。express

Spring具體的事務管理須要在配置文件中配置好PlatformTransactionManager,下面是不一樣類型的事務對應的Spring配置。編程

  • JDBC數據源的局部事務管理器的配置以下,
 1     <!-- 定義數據源Bean,使用C3P0數據源實現,並注入數據源的必要信息 -->
 2     <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSrouce"
 3         destroy-method="close"
 4         p:driverClass="com.mysql.jdbc.Driver"
 5         p:jdbcUrl="jdbc:mysql://localhost/test"
 6         p:user="root"
 7         p:password=""
 8         p:maxPoolSize="40"
 9         p:minPoolSize="2"
10         p:initialPoolSize="2"
11         p:maxIdleTime="30" />
12     <!-- 配置JDBC數據源的局部數據管理器,使用DataSourceTransactionManager類 -->
13     <bean id="transactionManager"
14         class="org.springframework.jdbc.datasource.DataSourceTransactionManager"
15         p:dataSource-ref="dataSource" />

 

  • 容器管理的JTA全局事務管理器的配置以下,
1     <bean id="dataSource" class="org.springframework.jndi.JndiObjectFactoryBean" 
2         p:jndiName="jdbc/jpetstore" />
3     <!-- 使用JtaTransactionManager類, 該類實現了PlatformTransactionManager接口 -->
4     <!-- 使用JTA全局事務,Spring容器能夠自行從Java EE服務器中獲取事務性資源,無需依賴注入 -->
5     <bean id="transactionManager" class="org.springframework.transaction.jta.JtaTransactionManager" />

 

對於JTA全局事務,只須要指定事務管理器的實現類JtaTransactionManager便可,Spring容器會自行從J2EE服務器獲取數據源,無需顯式注入進事務管理器。服務器

  • 基於Hibernate持久化技術的Spring局部事務配置以下,
 1     <!-- 定義數據源Bean,使用C3P0數據源實現,並注入數據源的必要信息 -->
 2     <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSrouce"
 3         destroy-method="close"
 4         p:driverClass="com.mysql.jdbc.Driver"
 5         p:jdbcUrl="jdbc:mysql://localhost/test"
 6         p:user="root"
 7         p:password=""
 8         p:maxPoolSize="40"
 9         p:minPoolSize="2"
10         p:initialPoolSize="2"
11         p:maxIdleTime="30" />
12     <!-- 定義Hibernate的SessionFactory, SessionFactory須要依賴數據源,注入dataSource -->
13     <bean id="sessionFactory"
14         class="org.springframework.orm.hibernate4.LocalSessionFactoryBean"
15         p:dataSource-ref="dataSource">
16         <!-- annotatedClasses用來列出所有持久化類 -->    
17         <property name="annotatedClasses">
18             <list>
19                 <!-- 如下用來列出全部PO類 -->
20                 <value>com.entity.User</value>
21             </list>
22         </property>
23         <!-- 定義Hibernate的sessionFactory屬性 -->
24         <property name="hibernateProperties">
25             <props>
26                 <!-- 指定Hibernate的鏈接方言 -->
27                 <prop key="hibernate.dialect">org.hibernate.dialect.MySQL5InnoDBDialect</prop>
28                 <!-- 是否根據Hibernate映射表建立數據表 -->
29                 <prop key="hibernate.hbm2ddl.auto">update</prop>
30             </props>
31         </property>
32     </bean>
33     <!-- 配置Hibernate的局部數據管理器,使用HibernateTransactionManager類 -->
34     <!-- 該類是PlatformTransactionManager接口針對Hibernate的特定實現 -->
35     <!-- 配置HibernateTransactionManager須要注入sessionFactory -->
36     <bean id="transactionManager"
37         class="org.springframework.orm.hibernate4.HibernateTransactionManager"
38         p:sessionFactory-ref="sessionFactory" />

 

Spring事務若是採用Hibernate策略,通常須要配置三點:數據源, sessionFactory, 事務管理器。

  • 若是底層採用Hibernate持久層技術,而事務採用JTA全局事務時,配置以下,
 1     <!-- 配置JTA數據源-->
 2     <bean id="dataSource" class="org.springframework.jndi.JndiObjectFactoryBean" 
 3         p:jndiName="jdbc/jpetstore" />
 4     <!-- 定義Hibernate的SessionFactory, SessionFactory須要依賴數據源,注入dataSource -->
 5     <bean id="sessionFactory"
 6         class="org.springframework.orm.hibernate4.LocalSessionFactoryBean"
 7         p:dataSource-ref="dataSource">
 8         <!-- annotatedClasses用來列出所有持久化類 -->    
 9         <property name="annotatedClasses">
10             <list>
11                 <!-- 如下用來列出全部PO類 -->
12                 <value>com.entity.User</value>
13             </list>
14         </property>
15         <!-- 定義Hibernate的sessionFactory屬性 -->
16         <property name="hibernateProperties">
17             <props>
18                 <!-- 指定Hibernate的鏈接方言 -->
19                 <prop key="hibernate.dialect">org.hibernate.dialect.MySQL5InnoDBDialect</prop>
20                 <!-- 是否根據Hibernate映射表建立數據表 -->
21                 <prop key="hibernate.hbm2ddl.auto">update</prop>
22             </props>
23         </property>
24     </bean>
25     <!-- 使用JtaTransactionManager類,該類是PlatformTransactionManager接口的實現類 -->
26     <!-- 針對全局事務管理的特定實現 -->
27     <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 <?xml version="1.0" encoding="UTF-8"?>
 2 <beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 3     xmlns="http://www.springframework.org/schema/beans"
 4     xmlns:p="http://www.springframework.org/schema/p"
 5     xmlns:aop="http://www.springframework.org/schema/aop"
 6     xmlns:tx="http://www.springframework.org/schema/tx"
 7     xsi:schemaLocation="http://www.springframework.org/schema/beans
 8     http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
 9     http://www.springframework.org/schema/aop
10     http://www.springframework.org/schema/aop/spring-aop-4.0.xsd
11     http://www.springframework.org/schema/tx
12     http://www.springframework.org/schema/tx/spring-tx-4.0.xsd">
13     <!-- 定義數據源Bean,使用C3P0數據源實現,並注入數據源的必要信息 -->
14     <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"
15         destroy-method="close"
16         p:driverClass="com.mysql.jdbc.Driver"
17         p:jdbcUrl="jdbc:mysql://localhost/test?useUnicode=true&amp;characterEncoding=UTF-8"
18         p:user="root"
19         p:password=""
20         p:maxPoolSize="40"
21         p:minPoolSize="2"
22         p:initialPoolSize="2"
23         p:maxIdleTime="30" />
24     <!-- 配置JDBC數據源的局部數據管理器,使用DataSourceTransactionManager類 -->
25     <bean id="transactionManager"
26         class="org.springframework.jdbc.datasource.DataSourceTransactionManager"
27         p:dataSource-ref="dataSource" />
28     
29     <!-- 配置一個業務邏輯Bean -->
30     <bean id="newsDao" class="com.dao.impl.NewsDaoImpl" p:ds-ref="dataSource" />
31     <!-- 配置事務加強處理, 指定事務管理器 -->
32     <tx:advice id="txAdvice"
33         transaction-manager="transactionManager">
34         <!-- 用於配置詳細的事務定義 -->
35         <tx:attributes>
36             <!-- 全部以get開頭的方法都是隻讀的 -->
37             <tx:method name="get*" read-only="true" />
38             <!-- 其餘方法默認都適用事務,指定超時5秒 -->
39             <tx:method name="*" isolation="DEFAULT" propagation="REQUIRED" timeout="5" />
40         </tx:attributes>
41     </tx:advice>
42     <aop:config>
43         <!-- 配置一個切入點,匹配impl包下全部以impl結尾的類裏的全部方法的執行 -->
44         <aop:pointcut expression="execution(* com.dao.impl.*Impl.*(..))" id="myPointcut" />
45         <!-- 將切入點myPointcut和加強txAdvice綁定-->
46         <aop:advisor advice-ref="txAdvice" pointcut-ref="myPointcut" />
47         <!-- 再配置一個切入點,匹配impl包下全部以abc開頭類裏的全部方法的執行  -->
48     </aop:config>
49 </beans>

NewsDaoImpl代碼中,則是插入重複數據到表中,

 1 package com.dao.impl;
 2 
 3 import javax.sql.DataSource;
 4 import org.springframework.jdbc.core.JdbcTemplate;
 5 import com.dao.NewsDao;
 6 
 7 public class NewsDaoImpl implements NewsDao {
 8     private DataSource ds;
 9     public void setDs(DataSource ds) {
10         this.ds = ds;
11     }
12     @Override
13     public void insert(String title, String content) {
14         //c3p0數據池的用法
15         JdbcTemplate jt = new JdbcTemplate(ds);
16         jt.update("insert into news_inf" + " values(100,?,?)", title, content);
17         jt.update("insert into news_inf" + " values(100,?,?)", title, content);
18         //若是沒有事務控制,則第一條記錄能夠被插入
19         //若是增長事務控制,將發現第一條記錄也插不進去
20     }
21 }

 

下面是測試方法,

1     public static void test3() {
2         ApplicationContext ctx = new ClassPathXmlApplicationContext("beans4JDBC.xml");
3         //獲取事務代理Bean
4         NewsDao dao = (NewsDao)ctx.getBean("newsDao", NewsDao.class);
5         dao.insert("java編程核心思想", "輕量級Java EE開發");
6         System.out.println("執行完畢");
7     }

 

執行測試方法會發現拋出異常(由於有重複數據),而又由於事務控制,數據庫中講不會有數據插入。

能夠看到上面例子中,一般對於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切入配置就能夠只經過一步(直接經過註解配置在方法名上)完成了。

<tx:annotation-driven transaction-manager="transactionManager" />

 

NewsDaoImpl.

1     @Transactional(propagation=Propagation.REQUIRED, isolation=Isolation.DEFAULT, timeout=5)
2     @Override
3     public void insert(String title, String content) {

 

 

Spring事務機制涉相關連接

http://www.mamicode.com/info-detail-1248286.html 

http://kingj.iteye.com/blog/1680350

相關文章
相關標籤/搜索