Spring事物抽象的关键是事物策略的概念。事物策略由org.springframework.transaction.PlatformTransactionManager接口定义。
public interface PlatformTransactionManager {
TransactionStatus getTransaction(
TransactionDefinition definition) throws TransactionException;
void commit(TransactionStatus status) throws TransactionException;
void rollback(TransactionStatus status) throws TransactionException;
}
这是一个主要的业务提供器接口(SPI),虽然可以在你的应用程序代码中使用程序化编程。因为PlatformTransactionManager是一个接口,其可以很容器根据需要仿制或存根。其没有绑定到一个查找策略,比如JNDI。PlatformTransactionManager的实现类就如同SpringIoC容器中的其他对象或者bean一样。这使得Spring框架事物抽象即使在与JTA使用时也同样受益。事物代码将比直接使用JTA更加容易测试。
继续遵循Spring的优良传统,TransactionException异常可以被任何PlatformTransactionManager接口的方法(非检查型,继承了java.lang.runnable接口)的抛出。事物的基础设施故障几乎总是致命的。在从事物失败中确实恢复中的应用程序代码中,应用程序开发者可以仍旧选择抓取和处理TransactionException。突出点是开发者不必强制这样处理这些异常。
getTransaction(。。。)方法返回一个TransactionStatus对象,这取决于TransactionDefinition参数。返回的TransactionStatus可能表示一个新的事物,或者代表一个存在的事物,如果一个匹配的事物存在于当前调用栈中。在后面例子中显示的,如果使用JavaEE事物上下文,TransactionStatus与执行线程关联。
TransactionDefinition接口指定了如下设置:
- Isolation(隔离性):这个事物与其他事物工作的隔离程序。例如,这个事物是否可以看见其他事物提交的写操作?
- Propagation(传播性):一般地,在一个事物范围内运行的所有代码将在这个事物中运行。然而,你知道当你一个事物上下文已经存在时,执行事物方法的事件中要指定行为。例如,代码可以继续运行在存在的事物中(一般情况),或者存在的事物可以暂停并且创建一个新的事物。Spring提供了与EJB CMT相似的全部事物传播概念
- Timeout(超时):事物将运行多久,在超时和由指定底层事物回滚前。
- Read-only状态:当你的代码仅读而不是修改数据,可以使用只读事物。这个事物在一些情况下是一个很有用的选项,比如当你使用Hibernate的时候。
这些设置反映了标准饿事物概念。如果必要,还涉及到讨论事物隔离级别的资源和其他核心事物概念。理解这些概念对于使用Spring框架或者其他事物管理解决方案是很重要的。
TransactionStatus接口为事物代码控制事物执行和查询事物状态提供了一个简单的方式。概念是相似的,如同事物APIs一样。
public interface TransactionStatus extends SavepointManager {
boolean isNewTransaction();
boolean hasSavepoint();
void setRollbackOnly();
boolean isRollbackOnly();
void flush();
boolean isCompleted();
}
不管在Spring中选择声明式或者编程式事物管理,定义正确的PlatformTransactionManager实现类是绝对重要的。一般通过依赖注入定义这个实现。
PlatformTransactionManager实现通常要求了解其工作的环境:JDBC,JTA,Hibernate等等。下面的例子显示了如何定义一个本地的PlatformTransactionManager实现(这个例子的工作环境是什么都没有的JDBC)。
你定义一个JDBC的DataSource:
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
<property name="driverClassName" value="${jdbc.driverClassName}" />
<property name="url" value="${jdbc.url}" />
<property name="username" value="${jdbc.username}" />
<property name="password" value="${jdbc.password}" />
</bean>
关联的PlatformTransactionManager bean定义稍后将有一个DataSource的引用。将如下所示:
<bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
如果在JavaEE容器章使用JTA,稍后你使用一个容器DataSource,通过JNDI获取,与Spring的JtaTransactionManager连接。下面就是JTA和JNDI查找的形式:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:jee="http://www.springframework.org/schema/jee"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/jee
http://www.springframework.org/schema/jee/spring-jee.xsd">
<jee:jndi-lookup id="dataSource" jndi-name="jdbc/jpetstore"/>
<bean id="txManager" class="org.springframework.transaction.jta.JtaTransactionManager" />
<!-- other <bean/> definitions here -->
</beans>
JtaTransactionManager不需要知道DataSource,或者其他指定的资源,因为其使用容器的全局事物管理机制。
注意:上述的dataSource bean定义使用来自jee命名空间的<jndi-lookup/>标签。
你也可以很容器的使用Hibernate的本地事物,如同下面例子显示的那样。在这个例子中,你需要定义一个Hibernate的LocalSessionFactoryBean,这样你的应用程序代码使用它获取Hibernate Session实例。
DataSource bean定义与前面显示的本地JDBC例子相同,那么在下面的例子中将不再显示。
注意:如果DataSource,被任何非JTA事物管理器使用的,通过JNDI查找并且由JavaEE容器管理。那么其应该是非事物的,因为Spring框架,而不是Java EE容器将管理这个事物。
这个例子中的Manager bean是HibernateTransactionManager类型的。如同DataSourceTransactionManager一样,需要一个DataSource的引用,HibernateTransactionManager 需要一个SessionFactory的引用。
<bean id="sessionFactory" class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="mappingResources">
<list>
<value>org/springframework/samples/petclinic/hibernate/petclinic.hbm.xml</value>
</list>
</property>
<property name="hibernateProperties">
<value>
hibernate.dialect=${hibernate.dialect}
</value>
</property>
</bean>
<bean id="txManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager">
<property name="sessionFactory" ref="sessionFactory" />
</bean>
如果你使用Hibernate和Java EE管理的JTA事物,那么你应该简单地使用前面JTA例子针对JDBC的相同的JtaTransactionManager。
<bean id="txManager" class="org.springframework.transaction.jta.JtaTransactionManager"/>
注意:如果你使用JTA,那么你的事物管理器定义将查找你使用的所有数据访问技术,不管是JDBC,Hibernate JPA或者任何其他支持的技术。这是基于JTA事物是全局事物的事实,其将查找所有的事物资源。
在所有的这些例子中,应用程序代码不需要改变。你可以改变的是事物如何仅仅通过改变配置来进行管理,即使这些改变意味着全本地向全局事物变化,反之亦然。