Spring的编程式事务和声明式事务详解
入口(了解一些基本概念) Spring事务属性(事务的属性有哪些?) 我们都知道事务有开始,保存点,提交,回滚,隔离级别等属性。那么Spring对于事务属性定义有哪些呢?通过TransactionDefinition接口我们可以了解到: public interface TransactionDefinition{ int getIsolationLevel(); int getPropagationBehavior(); int getTimeout(); boolean isReadOnly(); } 获取隔离级别 获取传播特性 获取超时 获取是否只读 事务隔离级别 隔离离别也是通过TransactionDefinition接口定义的,代表并发事务的隔离程度。
* 事务传播行为 所谓事务的传播行为是指,如果在开始当前事务之前,一个事务上下文已经存在,此时有若干选项可以指定一个事务性方法的执行行为。在TransactionDefinition定义中包括了如下几个表示传播行为的常量.
* 事务超时 指定事务的最大运行时间。使用int指定,单位是秒。 * 事务的只读属性 事务的只读属性是指,对事务性资源进行只读操作或者是读写操作。所谓事务性资源就是指那些被事务管理的资源,比如数据源、 JMS 资源,以及自定义的事务性资源等等。如果确定只对事务性资源进行只读操作,那么我们可以将事务标志为只读的,以提高事务处理的性能。在 TransactionDefinition 中以 boolean 类型来表示该事务是否只读。 * 事务的回滚规则 默认出现RuntimeException就会回滚。如果没有抛出任何异常,或者抛出了已检查异常,则仍然提交事务。这通常也是大多数开发者希望的处理方式,也是 EJB 中的默认处理方式。(这里个人理解的是已检查异常,是我们定义的checkedException,包括我们自定义的异常和调用方法捕获的异常) Spring事务的三个基本类 Spring 框架中,涉及到事务管理的 API 大约有100个左右,其中最重要的有三个:TransactionDefinition、PlatformTransactionManager、TransactionStatus。所谓事务管理,其实就是“按照给定的事务规则来执行提交或者回滚操作”。“给定的事务规则”就是用 TransactionDefinition 表示的,“按照……来执行提交或者回滚操作”便是用 PlatformTransactionManager 来表示,而 TransactionStatus 用于表示一个运行着的事务的状态。打一个不恰当的比喻,TransactionDefinition 与 TransactionStatus 的关系就像程序和进程的关系。 TransactionDefinition 定义事务的属性 TransactionStatus 定义事务的状态 public interface TransactionStatus{ boolean isNewTransaction(); void setRollbackOnly(); boolean isRollbackOnly(); } PlatformTransactionManager 就是各种事务平台的实现接口 Public interface PlatformTransactionManager{ TransactionStatus getTransaction(TransactionDefinition definition) throws TransactionException; void commit(TransactionStatus status)throws TransactionException; void rollback(TransactionStatus status)throws TransactionException; } 所以我们现在可以向下,spring的事务真正实现是PlatformTransactionManager的实现类,通过各种参数来符合TransactionDefinition和TransactionStatus的要求,最后根据我们编程的或者声明的进行事务管理。 根据底层框架的不同(稍后我们看一下DataSourceTransactoinManager和HibernateTransactionManager的代码,主要做了什么),Spring(或者其他框架)提供主要的实现如下: DataSourceTransactionManager: 适合JDBC和ibatis HibernateTransactionManager: 适合hibernate JpaTransactionManager: 适用于使用JPA进行数据持久化操作的情况(更底层的一些) 适用于使用JPA进行数据持久化操作的情况 到这里基本概念终于结束了,我们可以介绍两种类型的事务管理了 编程式事务管理 首先我们回想一下不适用spring事务管理时,hibernate事务的管理是怎么样的? 大概是我们手动获取session,获取transaction,开始transaction,提交或者回滚,关闭session 那么我们使用spring的管理之后,事务本身控制还是交给持久框架自己管理。知识spring像一个代理人一样,你告诉它,它之后转化后告诉底层框架。 看个实际例子吧: 基于底层API的编程式事务管理 public class BankServiceImpl implements BankService { private BankDao bankDao; private TransactionDefinition txDefinition; // transaction定义是哪个 private PlatformTransactionManager txManager; //具体使用的txmanager ...... public boolean transfer(Long fromId, Long toId, double amount) { //这里获取事务状态 TransactionStatus txStatus = txManager.getTransaction(txDefinition); boolean result = false; try { result = bankDao.transfer(fromId, toId, amount); //提交事务 txManager.commit(txStatus); } catch (Exception e) { result = false; //回滚 txManager.rollback(txStatus); System.out.println("Transfer Error!"); } return result; } } 对应的xml文件: <bean id="bankService" class="footmark.spring.core.tx.programmatic.origin.BankServiceImpl"> <property name="bankDao" ref="bankDao"/> <property name="txManager" ref="transactionManager"/> <property name="txDefinition"> <bean class="org.springframework.transaction.support.DefaultTransactionDefinition"> <property name="propagationBehaviorName" value="PROPAGATION_REQUIRED"/> </bean> </property> </bean> 但是这种写法和我们不适用spring的有何不同呢,最多是将事务层次提高了service层,不限于dao层,所以Spring做了改进: TransactionTemplate的execute方法提供一个内部匿名类,用来我们写transaction代码,然后提供一个transactionStatus的参数,这样你可以控制回滚。这样一来,我们就不用写任何关于事务API的代码了。格式大概是 Boolean b = transactionTempate.execute(new TransactionCallBack() { 执行方法(TransactionStatus transactionStatus){} },当执行完成后返回一个boolean的值. 还有一个方法,就是不提供返回结果的。 public class BankServiceImpl implements BankService { private BankDao bankDao; private TransactionTemplate transactionTemplate; ...... public boolean transfer(final Long fromId, final Long toId, final double amount) { //调用一个回调函数 return (Boolean) transactionTemplate.execute(new TransactionCallback(){ public Object doInTransaction(TransactionStatus status) { Object result; try { result = bankDao.transfer(fromId, toId, amount); } catch (Exception e) { status.setRollbackOnly(); result = false; System.out.println("Transfer Error!"); } return result; } }); } } 对应的XML: <bean id="bankService" class="footmark.spring.core.tx.programmatic.template.BankServiceImpl"> <property name="bankDao" ref="bankDao"/> <property name="transactionTemplate" ref="transactionTemplate"/> </bean> 从结果来看,好像还是不够简单和清晰。下面我们来看声明式事务管理,也是比较推荐的方式 声明式事务管理 Spring的声明式事务管理是基于AOP的,在方法前和后加上切点,用来打开事务和提交/回滚事务。 基于TransactionInterceptor的管理 最初,Spring 提供了 TransactionInterceptor 类来实施声明式事务管理功能 <beans...> ...... <bean id="transactionInterceptor" class="org.springframework.transaction.interceptor.TransactionInterceptor"> <property name="transactionManager" ref="transactionManager"/> <property name="transactionAttributes"> <props> //指定了方法,可以使用通配符 <prop key="transfer">PROPAGATION_REQUIRED</prop> </props> </property> </bean> <bean id="bankServiceTarget" class="footmark.spring.core.tx.declare.origin.BankServiceImpl"> <property name="bankDao" ref="bankDao"/> </bean> <bean id="bankService" class="org.springframework.aop.framework.ProxyFactoryBean"> <property name="target" ref="bankServiceTarget"/> <property name="interceptorNames"> <list> <idref bean="transactionInterceptor"/> </list> </property> </bean> ...... </beans> 首先,我们配置了一个 TransactionInterceptor 来定义相关的事务规则,他有两个主要的属性:一个是 transactionManager,用来指定一个事务管理器,并将具体事务相关的操作委托给它;另一个是 Properties 类型的 transactionAttributes 属性,它主要用来定义事务规则,该属性的每一个键值对中,键指定的是方法名,方法名可以使用通配符,而值就表示相应方法的所应用的事务属性。 指定事务属性的取值有较复杂的规则,这在 Spring 中算得上是一件让人头疼的事。具体的书写规则如下: 传播行为 [,隔离级别] [,只读属性] [,超时属性] [不影响提交的异常] [,导致回滚的异常] 基于 TransactionProxy… 的声明式事务管理 前面的声明式事务虽然好,但是却存在一个非常恼人的问题:配置文件太多。 为了缓解这个问题,Spring 为我们提供了 TransactionProxyFactoryBean,用于将TransactionInterceptor 和 ProxyFactoryBean 的配置合二为一 <beans......> ...... <bean id="bankServiceTarget" class="footmark.spring.core.tx.declare.classic.BankServiceImpl"> <property name="bankDao" ref="bankDao"/> </bean> <bean id="bankService" class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean"> <property name="target" ref="bankServiceTarget"/> <property name="transactionManager" ref="transactionManager"/> <property name="transactionAttributes"> <props> <prop key="transfer">PROPAGATION_REQUIRED</prop> </props> </property> </bean> ...... </beans> 这样子是减少了proxy的代码,但是每个service还是需要一个配置。所以我们可以使用自动代理的配置,这样子就减少了大量的配置。也应该是最常用的。 !-- Spring事务管理 --> <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource" /> </bean> <!-- 配置事务的传播特性 --> <bean id="baseTransactionProxy" class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean" abstract="true" > <property name="transactionManager" ref="transactionManager" /> <property name="transactionAttributes"> <props> <prop key="add*">PROPAGATION_REQUIRED</prop> <prop key="edit*">PROPAGATION_REQUIRED</prop> <prop key="remove*">PROPAGATION_REQUIRED</prop> <prop key="insert*">PROPAGATION_REQUIRED</prop> <prop key="update*">PROPAGATION_REQUIRED</prop> <prop key="del*">PROPAGATION_REQUIRED</prop> <prop key="*">readOnly</prop> </props> </property> </bean> 基于 命名空间的声明式事务管理 前面两种声明式事务配置方式奠定了 Spring 声明式事务管理的基石。在此基础上,Spring 2.x 引入了 命名空间,结合使用 命名空间,带给开发人员配置声明式事务的全新体验,配置变得更加简单和灵活。另外,得益于 命名空间的切点表达式支持,声明式事务也变得更加强大。 具体例子: <beans......> ...... <bean id="bankService" class="footmark.spring.core.tx.declare.namespace.BankServiceImpl"> <property name="bankDao" ref="bankDao"/> </bean> <tx:advice id="bankAdvice" transaction-manager="transactionManager"> <tx:attributes> <tx:method name="transfer" propagation="REQUIRED"/> </tx:attributes> </tx:advice> <aop:config> <aop:pointcut id="bankPointcut" expression="execution(* *.transfer(..))"/> <aop:advisor advice-ref="bankAdvice" pointcut-ref="bankPointcut"/> </aop:config> ...... </beans> @Transactional 的声明式事务管理 除了基于命名空间的事务配置方式,Spring 2.x 还引入了基于 Annotation 的方式,具体主要涉及@Transactional 标注。@Transactional 可以作用于接口、接口方法、类以及类方法上。当作用于类上时,该类的所有 public 方法将都具有该类型的事务属性,同时,我们也可以在方法级别使用该标注来覆盖类级别的定义。 具体例子: @Transactional(propagation = Propagation.REQUIRED) public boolean transfer(Long fromId, Long toId, double amount) { return bankDao.transfer(fromId, toId, amount); } 但是使用这种我们就必须启用tx的annotation: <tx:annotation-driven transaction-manager="transactionManager"/> 附录 DataSourceTransactionManager(类主要的方法,就不看原代码了) 设置和获取DataSource 获取transaction transaction是否存在 开始transaction 暂停,释放连接connection 恢复暂停的连接 提交 回滚 仅回滚 清理 … HibernateTransactionManager 转换异常 开始事务 清理 提交 获取事务 恢复 回滚 仅回滚 暂停 获取数据源 获取Entity的Interceptor 获取SessionFactory 是否存在事务 是否预先提交 总结 本文有关Spring的编程式事务和声明式事务详解的介绍就到这里,希望对大家有所帮助。有什么问题可以随时留言,小编会及时回复大家。在此也非常感谢大家对本站的支持! (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |