谈 Spring-Transaction(Spring事务管理 第二篇)
2012-08-15 11:40
393 查看
9.4. 使用资源同步的事务
现在应该比较清楚的是:不同的事务管理器是如何创建的,以及它们如何被连接到相应的需要被同步到事务的资源上(例如,DataSourceTransactionManager 对应到JDBCDataSource, HibernateTransactionManager 对应到Hibernate的
SessionFactory 等)。可是,剩下的问题是,直接或间接地使用一种持久化API(JDBC、Hibernate、JDO等)的应用代码,如何确保通过相关的
PlatformTransactionManager 来恰当地获取并操作资源,来满足事务同步,这些操作包括:创建、复用、清理 和 触发(可能没有)。
9.4.1. 高层次方案
首选的方法是使用Spring的高层持久化集成API。这种方式不会替换原始的API,而是在内部封装了资源创建、复用、清理、事务同步以及异常映射等功能,这样用户的数据访问代码就不必关心这些,而集中精力于自己的持久化逻辑。通常,对所有持久化API都采用这种模板 方法,包括 JdbcTemplate、HibernateTemplate和JdoTemplate类(这些在这份参考文档后面的章节中详细叙述)。
9.4.2. 低层次方案
在较低层次上,有以下这些类:DataSourceUtils(针对JDBC),SessionFactoryUtils(针对Hibernate),PersistenceManagerFactoryUtils(针对JDO)等等。当对应用代码来说,直接同原始持久化API特有的资源类型打交道是更好的选择时,这些类确保应用代码获取到正确的Spring框架所管理的bean,事务被正确同步,处理过程中的异常被映射到一致的API。例如,在JDBC环境下,你不再使用传统的调用 DataSource 的
getConnection() 方法的方式,而是使用Spring的 org.springframework.jdbc.datasource.DataSourceUtils,像这样:
Connection conn = DataSourceUtils.getConnection(dataSource);
如果已有一个事务及与之关联的connection存在,该实例将被返回。否则,该方法调用将触发起一个新的connection的创建动作,该connection(可选地)被同步到任何现有的事务,并可以在同一事务范围内被后续的调用复用。正如上面提到的,这个过程有一个额外的好处,就是任何
SQLException将被包装为Spring框架的
CannotGetJdbcConnectionException,该类是Spring框架的unchecked的DataAccessExceptions层次体系中的一员。这将给你比从
SQLException 中简单所得更多的信息,而且保证了跨数据库——甚至其他持久化技术——的移植性。
应该指出的是,这些类同样可以在没有Spring事务管理的环境中工作良好(事务同步能力是可选的),所以无论你是否使用Spring的事务管理,你都可以使用这些类。
当然,一旦你用过Spring的JDBC支持或Hibernate支持,你一般就不再会选择 DataSourceUtils 或是别的辅助类了,因为你会更乐意与Spring抽象一起工作,而不是直接使用相关的API。例如,如果你使用Spring的
JdbcTemplate 或 jdbc.object 包来简化使用JDBC,Spring会在幕后替你正确地获取连接,而你不需要写任何特殊代码。
9.4.3. TransactionAwareDataSourceProxy
工作在最底层的是 TransactionAwareDataSourceProxy 类。这是一个对目标DataSource 的代理,它包装了目标 DataSource,提供对Spring管理事务的可知性。在这点上,它类似于一个J2EE服务器提供的事务性JNDI
DataSource。
该类应该永远不需要被应用代码使用,除非现有代码存在需要直接传递一个标准的JDBC的 DataSource 的情况。这时可以通过参与Spring管理事务让这些代码仍然有用。书写新的代码时,首选的方法是采用上面提到的Spring高层抽象。
9.5. 声明式事务管理
大多数Spring用户选择声明式事务管理。这是对应用代码影响最小的选择,因此也最符合非侵入式 轻量级容器的理念。
Spring的声明式事务管理是通过Spring AOP实现的,因为事务方面的代码与Spring绑定并以一种样板式风格使用,不过尽管如此,你一般并不需要理解AOP概念就可以有效地使用Spirng的声明式事务管理。
从考虑EJB CMT和Spring声明式事务管理的相似以及不同之处出发是很有益的。它们的基本方法是相似的:都可以指定事务管理到单独的方法;如果需要可以在事务上下文调用
setRollbackOnly() 方法。不同之处在于:
不像EJB CMT绑定在JTA上,Spring声明式事务管理可以在任何环境下使用。只需更改配置文件,它就可以和JDBC、JDO、Hibernate或其他的事务机制一起工作。
Spring的声明式事务管理可以被应用到任何类(以及那个类的实例)上,不仅仅是像EJB那样的特殊类。
Spring提供了声明式的回滚规则:EJB没有对应的特性,我们将在下面讨论。回滚可以声明式的控制,不仅仅是编程式的。
Spring允许你通过AOP定制事务行为。例如,如果需要,你可以在事务回滚中插入定制的行为。你也可以增加任意的通知,就象事务通知一样。使用EJB CMT,除了使用setRollbackOnly(),你没有办法能够影响容器的事务管理。
Spring不提供高端应用服务器提供的跨越远程调用的事务上下文传播。如果你需要这些特性,我们推荐你使用EJB。然而,不要轻易使用这些特性。通常我们并不希望事务跨越远程调用。
TransactionProxyFactoryBean在哪儿?
Spring2.0及以后的版本中声明式事务的配置与之前的版本有相当大的不同。主要差异在于不再需要配置TransactionProxyFactoryBean了。
Spring2.0之前的旧版本风格的配置仍然是有效的;你可以简单地认为新的<tx:tags/>替你定义了TransactionProxyFactoryBean。
回滚规则的概念比较重要:它使我们能够指定什么样的异常(和throwable)将导致自动回滚。我们在配置文件中声明式地指定,无须在Java代码中。同时,我们仍旧可以通过调用
TransactionStatus 的 setRollbackOnly() 方法编程式地回滚当前事务。通常,我们定义一条规则,声明
MyApplicationException 必须总是导致事务回滚。这种方式带来了显著的好处,它使你的业务对象不必依赖于事务设施。典型的例子是你不必在代码中导入Spring API,事务等。
对EJB来说,默认的行为是EJB容器在遇到 系统异常(通常指运行时异常)时自动回滚当前事务。EJB CMT遇到
应用异常(例如,除了 java.rmi.RemoteException 外别的checked exception)时并不会自动回滚。默认式Spring处理声明式事务管理的规则遵守EJB习惯(只在遇到unchecked exceptions时自动回滚),但通常定制这条规则会更有用。
9.5.1. 理解Spring的声明式事务管理实现
本节的目的是消除与使用声明式事务管理有关的神秘性。简单点儿总是好的,这份参考文档只是告诉你给你的类加上@Transactional注解,在配置文件中添加('<tx:annotation-driven/>')行,然后期望你理解整个过程是怎么工作的。此节讲述Spring的声明式事务管理内部的工作机制,以帮助你在面对事务相关的问题时不至于误入迷途,回朔到上游平静的水域。![]() | Tip |
---|---|
阅读Spring源码是理解清楚Spring事务支持的一个好方法。Spring的Javadoc提供的信息丰富而完整。我们建议你在开发自己的Spring应用时把日志级别设为'DEBUG'级,这样你能更清楚地看到幕后发生的事。 |
![]() | Note |
---|---|
尽管使用Spring声明式事务管理不需要AOP(尤其是Spring AOP)的知识,但了解这些是很有帮助的。你可以在 Chapter 6, 使用Spring进行面向切面编程(AOP) 章找到关于Spring AOP的全部内容。 |
![](http://www.redsaga.com/spring_ref/2.0/html/images/tx.png)
9.5.2. 第一个例子
请看下面的接口和它的实现。这个例子的意图是介绍概念,使用 Foo 和Bar 这样的名字只是为了让你关注于事务的用法,而不是领域模型。
<!-- 我们想做成事务性的服务接口 --> package x.y.service; public interface FooService { Foo getFoo(String fooName); Foo getFoo(String fooName, String barName); void insertFoo(Foo foo); void updateFoo(Foo foo); }
<!-- 上述接口的一个实现 --> package x.y.service; public class DefaultFooService implements FooService { public Foo getFoo(String fooName) { throw new UnsupportedOperationException(); } public Foo getFoo(String fooName, String barName) { throw new UnsupportedOperationException(); } public void insertFoo(Foo foo) { throw new UnsupportedOperationException(); } public void updateFoo(Foo foo) { throw new UnsupportedOperationException(); } }
(对该例的目的来说,上例中实现类(DefaultFooService)的每个方法在其方法体中抛出
UnsupportedOperationException 的做法是恰当的,我们可以看到,事务被创建出来,响应
UnsupportedOperationException 的抛出,然后回滚。)
我们假定,FooService的前两个方法(getFoo(String)和getFoo(String, String))必须执行在只读事务上下文中,其余方法(insertFoo(Foo)和updateFoo(Foo))必须执行在读写事务上下文中。
使用XML方式元数据的声明式配置的话,你得这么写(不要想着一次全部理解,所有内容会在后面的章节详细讨论):
<!-- 'context.xml'文件的内容如下: --> <?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:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.0.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.0.xsd"> <!-- 这是我们将要配置并使它具有事务性的Service对象 --> <bean id="fooService" class="x.y.service.DefaultFooService"/> <!-- the transactional advice (i.e. what 'happens'; see the <aop:advisor/> bean below) --> <tx:advice id="txAdvice" transaction-manager="txManager"> <!-- the transactional semantics... --> <tx:attributes> <!-- all methods starting with 'get' are read-only --> <tx:method name="get*" read-only="true"/> <!-- other methods use the default transaction settings (see below) --> <tx:method name="*"/> </tx:attributes> </tx:advice> <!-- ensure that the above transactional advice runs for any execution of an operation defined by the FooService interface --> <aop:config> <aop:pointcut id="fooServiceOperation" expression="execution(* x.y.service.FooService.*(..))"/> <aop:advisor advice-ref="txAdvice" pointcut-ref="fooServiceOperation"/> </aop:config> <!-- don't forget the DataSource --> <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close"> <property name="driverClassName" value="oracle.jdbc.driver.OracleDriver"/> <property name="url" value="jdbc:oracle:thin:@rj-t42:1521:elvis"/> <property name="username" value="scott"/> <property name="password" value="tiger"/> </bean> <!-- similarly, don't forget the (particular) PlatformTransactionManager --> <bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource"/> </bean> <!-- other <bean/> definitions here --> </beans>
相关文章推荐
- Spring学习-32:Spring中的事务管理之声明式事务(原始方式,基于TransactionProxyBean)
- 谈 Spring-Transaction(Spring事务管理 第五篇)
- Spring的事务管理入门:编程式事务管理(TransactionTemplate)
- spring声明式管理事务+ hibernate + save is not valid without active transaction
- spring 编程式事务管理—PlatformTransactionManager
- Spring中使用DataSourceTransactionManager进行事务管理的xml配置
- Spring的事务管理入门:编程式事务管理(TransactionTemplate)!!!
- Spring Transaction + MyBatis SqlSession事务管理机制[marked]
- spring管理hibernate事务报异常--Transaction not successfully started解决方法
- SpringMVC 事务管理(DataSourceTransactionManager)spring的相关配置
- spring_hibernate_Transaction:事务管理
- Spring 声明式事务管理----基于TransactionProxyFactoryBean的方式
- Spring 事务管理 DataSourceTransactionManager 和 DataSourceTransactionManager
- Spring声明式事务管理(基于TransactionProxyFactoryBean)
- spring声明式事务管理(基于TransactionProxyFactoryBean的方式)
- spring 编程式事务管理—PlatformTransactionManager
- spring 管理 hibernate 事务 load is not valid without active transaction
- Spring事务管理----声明式:利用TransactionProxyFactoryBean生成事务代理
- hibernate,spring管理事务中(transaction,JDBC connection,Hibernate Session的使用)
- 谈 Spring-Transaction(Spring事务管理 第一篇)