8.6.1 Spring支持的事务策略
Java EE应用的传统事务有两种策略:全局事务和局部事务。全局事务由应用服务器管理,需要底层服务器的JTA(Java Transaction API)支持。局部事务和底层所采用的持久化技术有关,当采用JDBC持久化技术时,需要使用Connection队形来操作事务;而采用Hibernate持久化技术时,需要使用Session对象来操作事务。
全局事务可以跨多个事务性资源(典型例子是关系数据库和消息队列);使用局部事务,应用服务器不需要参与事务管理,因此不能保证跨多个事务性资源的事务的正确性。当然,实际上大部分应用都使用单一的事务性资源。
当采用传统的事务编程策略时,程序代码必然和具体的事务操作代码耦合,这样造成的后果是:当应用需要在不同的事务策略之间切换时,不须手动修改程序代码。如果使用Spring事务管理策略,就可以改变这种现状。
Spring 事务策略是通过PlatformTransactionManager接口体现的,该接口是Spring事务策略的核心。
该接口的源代码如下:
package edu.pri.lime._8_6_1.sourcecode; import org.springframework.transaction.TransactionDefinition; import org.springframework.transaction.TransactionException; import org.springframework.transaction.TransactionStatus; public interface PlatformTransactionManager { // 平台无关的获得事务的方法 TransactionStatus getTransaction(TransactionDefinition definition) throws TransactionException; // 平台无关的事务提交方法 void commit(TransactionStatus status) throws TransactionException; // 平台无关的事务回滚方法 void rollback(TransactionStatus status) throws TransactionException; }
PlatformTransactionManager是一个与任何事务策略分离的接口,随着底层不同事务策略的切换,应用必须采用不同的实现类。PlatformTransactionManager接口没有与任何事务性资源捆绑在一起,它可以适应于任何事务策略,结合Spring的IoC容器,可以向PlatformTransactionManager注入相关的平台特性。
PlatformTransactionManager接口有许多不同的实现类,应用程序面向与平台无关的接口编程,当底层采用不同的持久层技术时,系统只需要使用不同的PlatformTransactionManager实现类即可 ------ 而这种切换通常由Spring容器负责管理,应用程序即无须与具体的事务API耦合,也无须与特定实现类耦合,从而将应用和持久化技术、事务API彻底分离开来。
提示:
Spring 的事务机制是一种典型的策略模式,PlatformTransactionmanager代表事务管理接口,但它并不知道底层到底如何管理事务,它只要求事务管理需要提供开始事务(getTransaction())、提交事务(commit())和回滚事务(rollback())三个方法,但具体如何实现则交给其实现类来完成 ------ 不同的实现类则代表不同的事务管理策略。
即使使用容器管理的JTA,代码也依然无须执行JNDI查找,无须与特定的JTA资源耦合在一起,通过配置文件,JTA资源传给PlatformTransactionManager的实现类。因此,程序的代码可在JTA事务管理和非JTA事务管理之间轻松切换。
注意:
Spring完全支持夸多个事务性资源的全局事务,前提是底层的应用服务器(如WebLogic、JBoss等)支持JTA全局事务。可以这样说:Spring本身并没有任何事务支持,它只是负责包装底层的事务 ------ 应用程序面向PlatformTransactionManager接口编程时,Spring在底层负责将这些操作转换成具体的事务操作代码,因此应用的底层支持怎样的事务策略,那么Spring就可支持怎样的事务策略。Spring事务管理的优势是将应用从具体的事务API中分离出来,而不是真正提供事务管理的底层实现。
在PlatformTransactionManager接口内,包含一个getTransaction(TransactionDefinition definition)方法,该方法根据TransactionDefinition参数返回一个TransactionStatus对象。TransactionStatus对象表示一个事务,TransactionStatus被关联在当前执行的线程上。
getTransaction(TransactionDefinition definition)返回的TransactionStatus对象,可能是一个新的事务,也可能是一个已经存在的事务对象。如果当前执行的线程已经处于事务管理下,则返回当前线程的事务对象;否则,系统将新建的一个事务对象后返回。
TransactionDefinition接口定义了一个事务规则,该接口必须指定如下几个属性值:
⊙ 事务隔离 : 当前事务和其他事务的隔离程度。例如,这个事务能否看到其他事务未提交的数据等。
⊙ 事务传播 : 通常,在事务中执行的代码都会在当前事务中运行。但是,如果一个事务上下文已经存在,有几个选项可指定该事务性方法的执行行为。例如,在大多数情况下,简单地在现有的事务上下文中运行;或者挂起现有事务,创建一个新的事务。Spring提供EJB CMT(Contain Manager Transaction,容器管理事务)中所有的事务传播选项。
⊙ 事务超时 : 事务在超时前能运行多久,也就是事务的最长持续时间。如果事务一直没有被提交或回滚,将在超出该时间后,系统自动回滚事务。
⊙ 只读状态 : 只读事务不修改任何数据。在某些情况下(例如使用Hibernate时),只读事务是非常有用的优化。
TransactionStatus 代表事务本身,它提供了简单的事务执行和查询事务状态的方法,这些方法在所有事务API中都是相同的。
TransactionStatus接口的源代码如下:
package edu.pri.lime._8_6_1.sourcecode; public interface TransactionStatus { // 判断事务是否为新建的事务 boolean isNewTransaction(); // 设置事务回滚 void setRollBackOnly(); // 查询事务是否已有回滚标志 boolean isRollBackOnly(); }
Spring 具体的事务管理由PlatformTransactionManager的不同实现类来完成。在Spring容器中配置PlatformTransactionManger Bean时,必须针对不同的环境提供不同的实现类。
JDBC数据源的局部事务管理器的配置文件如下:
<?xml version="1.0" encoding="UTF-8"?> <!-- Spring 配置文件的根元素,使用Spring-beans-4.0.xsd语义约束 --> <beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://www.springframework.org/schema/beans" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd"> <!-- 扫描Spring的组件 --> <context:component-scan base-package="edu.pri.lime._8_6_1.jdbc.service.impl"/> <!-- 定义数据源Bean,使用C3P0数据源实现,并注入数据源的必要信息 --> <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource" destroy-method="close"> <property name="driverClass" value="com.mysql.jdbc.Driver"/> <property name="jdbcUrl" value="jdbc:mysql://localhost/spring"/> <property name="user" value="root"/> <property name="password" value="System"/> <property name="maxPoolSize" value="40"/> <property name="minPoolSize" value="2"/> <property name="initialPoolSize" value="2"/> <property name="maxidleTime" value="30"/> </bean> <!-- 配置JDBC数据源的局部事务管理器,使用DataSourceTransactionManager类 --> <!-- 该类实现PlatformTransactionManager接口,是针对采用数据源连接的特定实现 --> <!-- 配置DataSourceTransactionManager时需要依赖注入DataSource的引用 --> <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource"/> </bean> </beans>
容器管理的JTA全局事务管理器的配置文件如下:
<?xml version="1.0" encoding="UTF-8"?> <!-- Spring 配置文件的根元素,使用Spring-beans-4.0.xsd语义约束 --> <beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://www.springframework.org/schema/beans" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd"> <!-- 扫描Spring的组件 --> <context:component-scan base-package="edu.pri.lime._8_6_1.jta.service.impl"/> <!-- 配置JNDI数据源Bean,其中jndiName指定容器管理数据源的JNDI --> <bean id="dataSource" class="org.springframework.jndi.JndiObjectFactoryBean"> <property name="jndiName" value="jdbc/jpetstore"/> </bean> <!-- 使用JtaTransactionManager类,该类实现了PlatformTransactionManager接口 --> <!-- 针对采用全局事务管理的特定实现 --> <bean id="transactionManager" class="org.springframework.transaction.jta.JtaTransactionManager"/> <!-- 当配置JtaTransactionManager全局事务管理策略时,只需指定事务管理器实现类即可,无须传入额外的事务性资源。 --> <!-- 这是因为全局事务的JTA资源由Java EE服务器提供,而Spring容器能自行从Java EE服务器中获取该事务性资源,所以无须使用依赖注入来配置 --> </beans>
当采用Hibernate持久层访问策略时,局部事务策略的配置文件如下:
懒的写。留着以后配置成MyBatis
不论采用那种持久层反问技术,只要使用JTA全局事务,Spring事务管理的配置完全一样,因为它们采用的都是全局事务管理策略。
注意:
当采用JTA全局事务策略时,实际上需要底层应用服务器的支持,而不同的应用服务器所提供的JTA全局事务可能存在细节上的差异,因此实际配置全局事务管理器时可能需要使用JtaTransactionManager的子类,如OC4JJtaTransactionManager(Oracle提供的Java EE应用服务器)、WebLogicJtaTransactionManager(Oracle提供的WebLogic)、WebSphereUowTransactionManager(IBM提供的WebSphere)等,它们分别对应于不同的应用服务器。
综合三种配置文件来说,当应用程序采用Spring事务管理策略时,应用程序无须与具体的事务策略耦合,应用程序只要面向PlatformTransactionManager策略接口编程,ApplicationContext将会根据配置文件选择何时的事务策略实现类。
实际上,Spring提供了如下两种事务管理方式:
⊙ 编程式事务管理 : 及时使用Spring的编程式事务,程序也可直接获取容器中的TransactionManager Bean,该Bean忠实PlatformTransactionManager的实例,所以可以通过该接口提供的三个方法来开始事务、提交事务和回滚事务。
⊙ 声明式事务管理 : 无须在Java程序中书写任何事务操作代码,而是通过在XMl文件中为业务组件配置事务代理(AOP代理的一种),AOP为事务代理所织入的增强处理也由Spring提供 ------ 在目标方法执行之前,织入开始事务;在目标方法执行之后,织入结束事务。
不论采用何种持久化策略,Spring都提供了一致的事务抽象,因此,应用开发者能在任何环境下,使用一致的编程模型。无须更改代码,应用就可在不同的事务管理策略中切换。
当使用编程式事务时,开发者使用的是Spring事务抽象(面向PlatformTransactionManager接口编程),而无须使用任何具体的底层事务API。Spring的事务管理将代码从底层具体的事务API中抽象出来,该抽象能以任何底层事务为基础。
提示:
Spring的编程式事务还可通过TransactionTemplate类来完成,该类提供了一个execute(TransactionCallback action)方法,可以以更简捷的方式来进行事务操作。
当使用声明式事务时,开发者无须书写任何事务管理代码,不依赖Spring或任何其他事务API。Spring的声明式事务无须任何额外的容器支持,Spring容器本身管理声明式事务。使用声明式事务策略,可以让开发者更好地专注于业务逻辑的实现。
注意:
Spring 所支持的事务策略非常灵活,Spring的事务策略允许应用程序在不同的事务策略之间自由切换,及时需要在局部事务策略和全局事务策略之间切换,也只需修改配置文件即可,而应用程序的代码无须任何改变。这种灵活的设计,正是面向接口编程带来的优势。
啦啦啦
啦啦啦