一般有两种事务策略:全局事务和局部事务。全局事务能够跨多个事务性资源(即数据源,典型的是数据库和消息队列),一般都须要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