您的位置:首页 > 编程语言 > Java开发

Spring框架事务支持模型的优势

2016-05-15 19:54 411 查看

全局事务

全局事务支持对多个事务性资源的操作,通常是关系型数据库和消息队列。应用服务器通过JTA管理全局性事务,API非常烦琐。UserTransaction通常需要从JNDI获取,意味着需要与JNDI绑定在一起,且JTA一般只在应用服务器可用,降低了应用代码的可重用性。

本地事务

本地事务面向具体的资源,例如与JDBC连接关联的事务。本地事务易于使用,但不能跨多个事务性资源。使用JDBC管理事务的代码不能在全局JTA事务中运行,因此不能确保跨多个资源的正确性。且本地事务侵入了编程模型。

Spring的一致性编程模型

Spring让开发者在任何环境下都可以使用一致性的编程模型,编写一次代码,就可以在不同环境的不同事务管理策略中运行。Spring提供声明式和编程式事务管理,大多数人选择声明式,这也是推荐的方式。

使用编程式事务管理时,开发者使用的是Spring框架的事务抽象,它可以运行在任何底层事务设施之上。使用声明式事务管理,开发者只需要编写少量甚至不需要编写事务相关代码,因此应用代码不依赖Spring框架事务API或其它事务API。

Spring的事务管理支持从传统规则到应用服务器的转换。

通常只有在通过EJB进行声明式事务时才真的需要应用服务器。实际上即使应用服务器有JTA的功能,也可以认为Spring框架的声明式事务比EJBCMT更加强大,更加能提高效率。

只有在应用需要跨多个资源处理事务时才需要应用服务器的JTA功能。许多高级应用使用单个高可扩展的数据库(例如OracleRAC)来代替。独立的事务管理器也是一种方案,例如AtomikosTransactions、JOTM。应用也可能需要其它的应用服务器功能,例如JMS、JCA。

Spring框架让开发者决定何时把应用扩展为完整的应用服务器,只需要在配置文中修改某些Bean定义即可。

理解Spring框架的事务抽象

事务策略是Spring事务抽象的关键。事务策略由org.springframework.transaction.PlatformTransactionManager接口定义:

publicinterfacePlatformTransactionManager{
TransactionStatusgetTransaction(TransactionDefinitiondefinition)throwsTransactionException;
voidcommit(TransactionStatusstatus)throwsTransactionException;
voidrollback(TransactionStatusstatus)throwsTransactionException;
}


这是一个服务提供商接口(SPI),虽然可以在应用中编程式地使用。由于这是一个接口,所以可以很容易被模拟出来。它并不与JNDI这样的查找策略绑定在一起。它可以像其它bean一样在Spring框架IoC容器中被定义。即使在使用JTA的场景下,也可以从Spring框架的事务中受益,因为Spring中的事务代码比直接使用JTA的更加容易测试。

TransactionException是作为非检查异常抛出,它扩展java.lang.RuntimeException类,因为在Spring的角度看来,事务设施故障几乎总是致命的,很少情况下应用可以从事务故障中恢复。开发者可以选择捕获这个异常并作处理,但并不强制这样做。

getTransaction(…)根据TransactionDefinition类型的参数返回一个TransactionStatus对象,所返回的对象可能代表一个新的事务,也可能是一个当前调用栈中已经存在的事务。后一种情况表示,在使用JavaEE事务上下文时,一个TransactionStatus是与执行线程关联的。

TransactionDefinition接口

TransactionDefinition接口指定了:

隔离级别(Isolation):一个事务与其它事务的隔离级别,例如一个事务是否可看到其它事务未提交的写操作
传播性(Propagation):一般所有在一个事务中运行的代码都会处于一个事务中,但当执行一个方法并且事务环境已经存在时,可以指定此时的行为。例如所调用方法内的代码继续运行在已经存在的事务中(常见情况),或者暂停当前事务并创建一个新的事务来运行所调用方法内的代码。Spring提供了所有EJBCMT所提供的事务传播选项。
超时(Timeout):在事务超时并被底层事务设施自动回滚前,这个事务可以运行多久。
只读状态(Read-only):当代码不修改数据时,可以使用只读事务。只读事务在某些场景下很有用,例如使用Hibernate时。
以上设置是标准的事务概念,指的就是事务隔离级别及其它核心事务概念。理解这些概念是使用Spring框架事务管理或其它事务解决方案的基础。

TransactionStatus接口

TransactionStatus接口为事务性代码提供控制事务执行和查询事务状态的简便方式。这些概念必须要熟悉,它们是所有事务API都共同的东西:

publicinterfaceTransactionStatusextendsSavepointManager{
booleanisNewTransaction();
booleanhasSavepoint();
voidsetRollbackOnly();
booleanisRollbackOnly();
voidflush();
booleanisCompleted();
}


在Spring中不管理使用声明式事务还是编程式事务,定义正确的PlatformTransactionManager实现都是必须的,一般通过DI进行定义。

PlatformTransactionManager一般只需要知道自己的工作环境:JDBC、JTA、Hibernate等等。下面是定义一个本地PlatformTransactionManager实现的例子:

定义JDBC数据源:

<beanid="dataSource"class="org.apache.commons.dbcp.BasicDataSource"destroy-method="close">
<propertyname="driverClassName"value="${jdbc.driverClassName}"/>
<propertyname="url"value="${jdbc.url}"/>
<propertyname="username"value="${jdbc.username}"/>
<propertyname="password"value="${jdbc.password}"/>
</bean>

相关的PlatformTransactionManager引用DataSource定义:

<beanid="txManager"class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<propertyname="dataSource"ref="dataSource"/>
</bean>
如果在JavaEE容器中使用容器DataSource,则通过JNDI获取,并与Spring的JtaTransactionManager关联:


<?xmlversion="1.0"encoding="UTF-8"?>
<beansxmlns="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-lookupid="dataSource"jndi-name="jdbc/jpetstore"/>
<beanid="txManager"class="org.springframework.transaction.jta.JtaTransactionManager"/>
<!--other<bean/>definitionshere-->
</beans>


Hibernate事务配置


<beanid="sessionFactory"class="org.springframework.orm.hibernate5.LocalSessionFactoryBean">
<propertyname="dataSource"ref="dataSource"/>
<propertyname="mappingResources">
<list>
<value>org/springframework/samples/petclinic/hibernate/petclinic.hbm.xml</value>
</list>
</property>
<propertyname="hibernateProperties">
<value>
hibernate.dialect=${hibernate.dialect}
</value>
</property>
</bean>

<beanid="txManager"class="org.springframework.orm.hibernate5.HibernateTransactionManager">
<propertyname="sessionFactory"ref="sessionFactory"/>
</bean>


如果使用Hibernate和JavaEE容器管理的JTA事务,则与前面对JDBC的JTA配置一样:


<beanid="sessionFactory"class="org.springframework.orm.hibernate5.LocalSessionFactoryBean">
<propertyname="dataSource"ref="dataSource"/>
<propertyname="mappingResources">
<list>
<value>org/springframework/samples/petclinic/hibernate/petclinic.hbm.xml</value>
</list>
</property>
<propertyname="hibernateProperties">
<value>
hibernate.dialect=${hibernate.dialect}
</value>
</property>
</bean>


<beanid="txManager"class="org.springframework.transaction.jta.JtaTransactionManager"/>
如果使用JTA,则不管使用什么样的数据访问技术(JDBC、HibernateJPA、或其它支持的技术),事务管理器定义会以相同的方式进行查找,因为JTA事务是全局性的,它会证募所有的事务性资源(JDBCconnection、SessionFactory等等)。
通过这种配置方式,本地和全局事务的转换不需要修改应用代码,只需要在配置文件上做修改。

使用事务同步资源

高级别同步方式

优先使用的是Spring高级别的基于模板的持久化集成API,或使用带有事务感知的工厂Bean的原生ORMAPI,或者管理原生资源工厂的代理。这些事务感知的方案在内部处理对资源的创建、重用、清理、可选的资源事务同步、异常映射,因此用户代码不需要做这些工作,只需要关注于自身的持久化逻辑。一般对JDBC的访问使用的是JdbcTemplate模板方式,或ORM原生API。

低级别同步方式

JDBC的DataSourceUtils、JPA的EntityManagerFactoryUtils、Hibernate的SessionFactoryUtils、JDO的PersistemceManagerFactoryUtils是低级别的操作工具。如果应用代码需要直接处理原生持久化API的资源类型时,可以使用这些类来确保获取正确的由Spring框架管理的实例、事务被正确同步、异常被正确映射到一致的API。

例如对于传统的JDBC,可以使用下面的代码代替对DataSource的getConnection()方法的调用:

Connectionconn=DataSourceUtils.getConnection(dataSource);
上面的代码,如果一个已经存在的事务已经有一个同步connection与其关联,则这个connection会被返回,否则会创建一个新的连接,并且会同步到已经存在的事务,且在后继对同一个事务的使用中可以重用这个连接。上面调用中,底层抛出的SQLException被Spring框架包装成CannotGetJdbcConnectionException,一个非检查DataAccessException,这比单纯的SQLException能够提供更多的信息,并确保跨数据库甚至跨不同持久化持久的可移植性。
上面的代码在没有Spring事务管理的情况下也是可用的。
在使用Spring的JDBC支持、JPA支持、Hibernate支持时,多数人更愿意使用Spring的事务抽象,而不是直接使用类似DataSourceUtils这样的辅助类。例如使用JdbcTemplate来简化JDBC的使用,它会自动为应用获取正确的连接。

TransactionAwareDataSourceProxy

[code]在最低层存在一个TransactionAwareDataSourceProxy类,它是DataSource的代理类,包装了DataSource,以添加Spring事务管理的感知功能,类似于由JavaEE服务器提供的事务性JNDIDataSource。

除非现存代码必须通过传递一个标准的JDBCDataSource实现进行调用,否则这个类不应该被使用。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: