一般有兩種事務策略:全局事務和局部事務。全局事務能夠跨多個事務性資源(即數據源,典型的是數據庫和消息隊列),一般都須要J2EE應用服務器的管理,其底層須要服務器的JTA支持。而局部事務則與底層採用的持久化技術有關,若是底層直接使用JDBC,須要用Connection對象來操事務。若是採用Hibernate持久化技術,則須要使用session對象來操做事務。html
一般的,使用JTA事務,JDBC事務及Hibernate事務的編程流程大體以下,java
上圖也能夠看出,採用傳統事務編程,程序代碼必須和具體的事務策略的API耦合,若是應用須要切換一種策略,意味着須要大幅修改代碼。可是若是使用Spring事務的話,就不會有這個問題了。mysql
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配置。編程
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" />
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服務器獲取數據源,無需顯式注入進事務管理器。服務器
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, 事務管理器。
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配置文件能夠爲業務組件配置事務代理,事務代理爲業務組件提供事務控制。如今這種方式是最好的,源碼侵入性最低。
當使用聲明式事務時,只須要寫好配置文件,配置須要事務控制的組件種類,業務組件就會在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&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"
除了使用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