事务的第一个方面是传播行为(propagation behavior)。当事务方法被另外一个事务方法调用时,必须指定事务应该如何传播。例如:方法可能继续在现有事务中运行,也可能开启一个新事务,并在本身的事务中运行。html
Spring定义了七种传播行为:java
注:如下具体讲解传播行为的内容参考自Spring事务机制详解mysql
此@Transactional注解来自org.springframework.transaction.annotation包,而不是javax.transaction。程序员
Spring事务管理的实现有许多细节,若是对整个接口框架有个大致了解会很是有利于咱们理解事务,下面经过讲解Spring的事务接口来了解Spring实现事务的具体策略。
Spring事务管理涉及的接口的联系以下:spring
Spring并不直接管理事务,而是提供了多种事务管理器,他们将事务管理的职责委托给Hibernate或者JTA等持久化机制所提供的相关平台框架的事务来实现。
Spring事务管理器的接口是org.springframework.transaction.PlatformTransactionManager,经过这个接口,Spring为各个平台如JDBC、Hibernate等都提供了对应的事务管理器,可是具体的实现就是各个平台本身的事情了。此接口的内容以下:sql
Public interface PlatformTransactionManager()...{ // 由TransactionDefinition获得TransactionStatus对象 TransactionStatus getTransaction(TransactionDefinition definition) throws TransactionException; // 提交 Void commit(TransactionStatus status) throws TransactionException; // 回滚 Void rollback(TransactionStatus status) throws TransactionException; }
从这里可知具体的具体的事务管理机制对Spring来讲是透明的,它并不关心那些,那些是对应各个平台须要关心的,因此Spring事务管理的一个优势就是为不一样的事务API提供一致的编程模型,如JTA、JDBC、Hibernate、JPA。下面分别介绍各个平台框架实现事务管理的机制。数据库
若是应用程序中直接使用JDBC来进行持久化,DataSourceTransactionManager会为你处理事务边界。为了使用DataSourceTransactionManager,你须要使用以下的XML将其装配到应用程序的上下文定义中:编程
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource" /> </bean>
实际上,DataSourceTransactionManager是经过调用java.sql.Connection来管理事务,然后者是经过DataSource获取到的。经过调用链接的commit()方法来提交事务,一样,事务失败则经过调用rollback()方法进行回滚。session
若是应用程序的持久化是经过Hibernate实习的,那么你须要使用HibernateTransactionManager。对于Hibernate3,须要在Spring上下文定义中添加以下的<bean>声明:并发
<bean id="transactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager"> <property name="sessionFactory" ref="sessionFactory" /> </bean>
sessionFactory属性须要装配一个Hibernate的session工厂,HibernateTransactionManager的实现细节是它将事务管理的职责委托给org.hibernate.Transaction对象,然后者是从Hibernate Session中获取到的。当事务成功完成时,HibernateTransactionManager将会调用Transaction对象的commit()方法,反之,将会调用rollback()方法。
Hibernate多年来一直是事实上的Java持久化标准,可是如今Java持久化API做为真正的Java持久化标准进入你们的视野。若是你计划使用JPA的话,那你须要使用Spring的JpaTransactionManager来处理事务。你须要在Spring中这样配置JpaTransactionManager:
<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager"> <property name="sessionFactory" ref="sessionFactory" /> </bean>
JpaTransactionManager只须要装配一个JPA实体管理工厂(javax.persistence.EntityManagerFactory接口的任意实现)。JpaTransactionManager将与由工厂所产生的JPA EntityManager合做来构建事务。
若是你没有使用以上所述的事务管理,或者是跨越了多个事务管理源(好比两个或者是多个不一样的数据源),你就须要使用JtaTransactionManager:
<bean id="transactionManager" class="org.springframework.transaction.jta.JtaTransactionManager"> <property name="transactionManagerName" value="java:/TransactionManager" /> </bean>
JtaTransactionManager将事务管理的责任委托给javax.transaction.UserTransaction和javax.transaction.TransactionManager对象,其中事务成功完成经过UserTransaction.commit()方法提交,事务失败经过UserTransaction.rollback()方法回滚。
上面讲到的事务管理器接口PlatformTransactionManager经过getTransaction(TransactionDefinition definition)方法来获得事务,这个方法里面的参数是TransactionDefinition类,这个类就定义了一些基本的事务属性。
那么什么是事务属性呢?事务属性能够理解成事务的一些基本配置,描述了事务策略如何应用到方法上。事务属性包含了5个方面,如图所示:
而TransactionDefinition接口内容以下:
public interface TransactionDefinition { // 返回事务的传播行为 int getPropagationBehavior(); // 返回事务的隔离级别,事务管理器根据它来控制另一个事务能够看到本事务内的哪些数据 int getIsolationLevel(); // 返回事务必须在多少秒内完成 int getTimeout(); // 事务是否只读,事务管理器可以根据这个返回值进行优化,确保事务是只读的 boolean isReadOnly(); }
咱们能够发现TransactionDefinition正好用来定义事务属性,下面详细介绍一下各个事务属性。
在应用系统调用 事务声明 的目标方法时,Spring Framework 默认使用 AOP 代理,在代码运行时生成一个代理对象,根据事务配置信息,这个代理对象决定该声明事务的目标方法是否由拦截器 TransactionInterceptor 来使用拦截,在 TransactionInterceptor 拦截时,会在目标方法开始执行以前建立并加入事务,并执行目标方法的逻辑, 最后根据执行状况是否出现异常,利用抽象事务管理器 AbstractPlatformTransactionManager 操做数据源 DataSource 提交或回滚事务。
Spring AOP 代理有 CglibAopProxy 和 JdkDynamicAopProxy 两种,以CglibAopProxy 为例,对于CglibAopProxy,须要调用其内部类的DynamicAdvisedInterceptor 的 intercept 方法。对于 JdkDynamicAopProxy,则调用其 invoke 方法。
事务失效问题
在同一个代理对象内部,事务方法之间的直接嵌套调用,普通方法和事务方法之间的直接嵌套调用,都会形成事务异常!具体表现为某些传播行为不生效或者直接事务控制不生效。
@Service public class DemoService { @Transaction public void transactionMethod1() { op1(); op2(); ... } public void commonMethod(){ ... transactionMethod1(); //照顾基础不牢的朋友,这里至关于 this.transactionMethod1(); ... } @Transaction public void transactionMethod2(){ ... this.transactionMethod3(); ... } @Transaction(propagation= Propagation.REQUIRES_NEW) public void transactionMethod3() { op3(); ... } }
上面代码中,若是调用 DemoService 的 bean 对象的commonMethod ,则transactionMethod1里定义的事务将不生效(好比op2发生错误时,并不会回滚op1的操做),bean 调用 transactionMethod2时,transactionMethod2时里面调用的transactionMethod3也不会开启新的事务。
为何会这样?
上面的实现机制中讲到,AOP的实现都是经过动态代理来实现,而AOP限制了咱们只能在目标方法的开始和结束做为切点作切入处理加强。当动态代理对象最终调用的原始对象的目标方法时,并不能干预到目标方法内的方法调用行为,若是原始对象直接调用(用this.xxx方式)其余同类方法时,实际调用的是原始对象自身的方法,而不是代理类对象加强后(增长事务控制后)的方法。此时Spring对方法事务的控制(包括事务的传播行为、事务的隔离级别等)彻底失效。
如何解决?
要想解决此类问题,主要都在于原始对象在调用对象内其余方法时,不要使用this.xxx的方式直接调用,经过注入或者获取代理对象的方式,使用代理对象调用须要调用的方法。下面列举几个解决方式:
1.注入自身,使用代理对象调用
@Service public class DemoService { @Autowired DemoService demoService; @Transaction public void transactionMethod1() { op1(); op2(); ... } public void commonMethod() { ... //this.transactionMethod1() -> demoService.transactionMethod1() demoService.transactionMethod1(); ... } }
2.使用AopContext,获取当前代理对象
@Service public class DemoService { @Transaction public void transactionMethod1() { op1(); op2(); ... } public void commonMethod() { ... //this.transactionMethod1() -> ((DemoService)AopContext.currentProxy()).transactionMethod1();) ((DemoService)AopContext.currentProxy()).transactionMethod1(); ... } ... }
3.使用BeanFactory获取代理对象(代码略)