谈 Spring-Transaction(Spring事务管理 第五篇)

来源:互联网 发布:淘宝蒲公英 编辑:程序博客网 时间:2024/05/16 06:21

9.5.7. 插入事务操作

考虑这样的情况,你有一个类的实例,而且希望 同时插入事务性通知(advice)和一些简单的剖析(profiling)通知。那么,在<tx:annotation-driven/>环境中该怎么做?

我们调用 updateFoo(Foo) 方法时希望这样:

  • 配置的剖析切面(profiling aspect)开始启动,

  • 然后进入事务通知(根据配置创建一个新事务或加入一个已经存在的事务),

  • 然后执行原始对象的方法,

  • 然后事务提交(我们假定这里一切正常),

  • 最后剖析切面报告整个事务方法执行过程花了多少时间。

[Note]Note

这章不是专门讲述AOP的任何细节(除了应用于事务方面的之外)。请参考 Chapter 6, 使用Spring进行面向切面编程(AOP) 章以获得对各种AOP配置及其一般概念的详细叙述。

这里有一份简单的剖析切面(profiling aspect)的代码。(注意,通知的顺序是由 Ordered 接口来控制的。要想了解更多细节,请参考 Section 6.2.4.7, “通知(Advice)顺序” 节。)

package x.y;import org.aspectj.lang.ProceedingJoinPoint;import org.springframework.util.StopWatch;import org.springframework.core.Ordered;public class SimpleProfiler implements Ordered {    private int order;    // allows us to control the ordering of advice    public int getOrder() {        return this.order;    }    public void setOrder(int order) {        this.order = order;    }    // this method is the around advice    public Object profile(ProceedingJoinPoint call) throws Throwable {        Object returnValue;        StopWatch clock = new StopWatch(getClass().getName());        try {            clock.start(call.toShortString());            returnValue = call.proceed();        } finally {            clock.stop();            System.out.println(clock.prettyPrint());        }        return returnValue;    }}

这里是帮助满足我们上述要求的配置数据。

<?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">    <bean id="fooService" class="x.y.service.DefaultFooService"/>    <!-- this is the aspect -->    <bean id="profiler" class="x.y.SimpleProfiler">        <!-- execute before the transactional advice (hence the lower order number) -->                <property name="order" value="1"/>    </bean>        <tx:annotation-driven transaction-manager="txManager"/>    <aop:config>        <!-- this advice will execute around the transactional advice -->        <aop:aspect id="profilingAspect" ref="profiler">            <aop:pointcut id="serviceMethodWithReturnValue" expression="execution(!void x.y..*Service.*(..))"/>            <aop:around method="profile" pointcut-ref="serviceMethodWithReturnValue"/>        </aop:aspect>    </aop:config>    <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>    <bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">        <property name="dataSource" ref="dataSource"/>    </bean></beans>

上面配置的结果将获得到一个拥有剖析和事务方面的 按那样的顺序 应用于它上面的 'fooService' bean。 许多附加的方面的配置将一起达到这样的效果。

最后,下面的一些示例演示了使用纯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">    <bean id="fooService" class="x.y.service.DefaultFooService"/>    <!-- the profiling advice -->    <bean id="profiler" class="x.y.SimpleProfiler">        <!-- execute before the transactional advice (hence the lower order number) -->        <property name="order" value="1"/>    </bean>    <aop:config>        <aop:pointcut id="entryPointMethod" expression="execution(* x.y..*Service.*(..))"/>        <!-- will execute after the profiling advice (c.f. the order attribute) -->               <aop:advisor advice-ref="txAdvice" pointcut-ref="entryPointMethod"order="2"/>                 <!-- order value is higher than the profiling aspect -->        <aop:aspect id="profilingAspect" ref="profiler">            <aop:pointcut id="serviceMethodWithReturnValue" expression="execution(!void x.y..*Service.*(..))"/>            <aop:around method="profile" pointcut-ref="serviceMethodWithReturnValue"/>        </aop:aspect>    </aop:config>    <tx:advice id="txAdvice" transaction-manager="txManager">        <tx:attributes>            <tx:method name="get*" read-only="true"/>            <tx:method name="*"/>        </tx:attributes>    </tx:advice>    <!-- other <bean/> definitions such as a DataSource and a PlatformTransactionManager here --></beans>

上面配置的结果是创建了一个 'fooService' bean,剖析方面和事务方面被 依照顺序 施加其上。如果我们希望剖析通知在目标方法执行之前 后于 事务通知执行,而且在目标方法执行之后先于 事务通知,我们可以简单地交换两个通知bean的order值。

如果配置中包含更多的方面,它们将以同样的方式受到影响。

9.5.8. 结合AspectJ使用@Transactional

通过AspectJ切面,你也可以在Spring容器之外使用Spring框架的 @Transactional 功能。要使用这项功能你必须先给相应的类和方法加上@Transactional注解,然后把spring-aspects.jar 文件中定义的org.springframework.transaction.aspectj.AnnotationTransactionAspect 切面连接进(织入)你的应用。同样,该切面必须配置一个事务管理器。你当然可以通过Spring框架容器来处理注入,但因为我们这里关注于在Spring容器之外运行应用,我们将向你展示如何通过手动书写代码来完成。

[Note]Note

在我们继续之前,你可能需要好好读一下前面的Section 9.5.6, “使用 @Transactional” 和Chapter 6,使用Spring进行面向切面编程(AOP) 两章。

// construct an appropriate transaction manager DataSourceTransactionManager txManager = new DataSourceTransactionManager(getDataSource());// configure the AnnotationTransactionAspect to use it; this must be done before executing any transactional methodsAnnotationTransactionAspect.aspectOf().setTransactionManager (txManager); 
[Note]Note

使用此切面(aspect),你必须在 实现 类(和/或类里的方法)、而 不是 类的任何所实现的接口上面进行注解。AspectJ遵循Java的接口上的注解 不被继承 的规则。

类上的 @Transactional 注解指定了类里的任何 public 方法执行的默认事务语义。

类里的方法的 @Transactional 将覆盖掉类注解的默认事务语义(如何存在的话)。 publicprotected和默认可见的方法可能都被注解。直接对 protected和默认可见的方法进行注解,让这些方法在执行时去获取所定义的事务划分是唯一的途径。

要把 AnnotationTransactionAspect 织入你的应用,你或者基于AspectJ构建你的应用(参考AspectJ Development Guide),或者采取“载入时织入”(load-time weaving),参考Section 6.8.4, “在Spring应用中使用AspectJ Load-time weaving(LTW)” 获得关于使用AspectJ进行“载入时织入”的讨论。

9.6. 编程式事务管理

Spring提供两种方式的编程式事务管理:

  • 使用 TransactionTemplate

  • 直接使用一个 PlatformTransactionManager 实现

如果你选择编程式事务管理,Spring小组推荐你采用第一种方法(即使用 TransactionTemplate)。第二种方法类似使用JTA的UserTransaction API (除了异常处理简单点儿)。

9.6.1. 使用 TransactionTemplate

TransactionTemplate 采用与Spring中别的 模板 同样的方法,如 JdbcTemplateHibernateTemplate。它使用回调机制,将应用代码从样板式的资源获取和释放代码中解放出来,不再有大量的try/catch/finally/try/catch代码块。同样,和别的模板类一样,TransactionTemplate 类的实例是线程安全的。

必须在事务上下文中执行的应用代码看起来像这样:(注意使用 TransactionCallback 可以有返回值)

Object result = tt.execute(new TransactionCallback() {    public Object doInTransaction(TransactionStatus status) {        updateOperation1();        return resultOfUpdateOperation2();    }});

如果不需要返回值,更方便的方式是创建一个 TransactionCallbackWithoutResult 的匿名类,如下:

tt.execute(new TransactionCallbackWithoutResult() {    protected void doInTransactionWithoutResult(TransactionStatus status) {        updateOperation1();        updateOperation2();    }});

回调方法内的代码可以通过调用 TransactionStatus 对象的 setRollbackOnly() 方法来回滚事务。

想要使用 TransactionTemplate 的应用类必须能访问一个 PlatformTransactionManager(典型情况下通过依赖注入提供)。这样的类很容易做单元测试,只需要引入一个 PlatformTransactionManager 的伪类或桩类。这里没有JNDI查找、没有 静态 诡计,它是一个如此简单的接口。像往常一样,使用Spring给你的单元测试带来极大地简化。

9.6.2. 使用 PlatformTransactionManager

你也可以直接使用 org.springframework.transaction.PlatformTransactionManager的实现来管理事务。只需通过bean引用简单地传入一个PlatformTransactionManager 实现,然后使用TransactionDefinitionTransactionStatus 对象,你就可以启动一个事务,提交或回滚。

DefaultTransactionDefinition def = new DefaultTransactionDefinition();def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);TransactionStatus status = txManager.getTransaction(def);try {    // execute your business logic here}catch (MyException ex) {    txManager.rollback(status);    throw ex;}txManager.commit(status);

9.7. 选择编程式事务管理还是声明式事务管理

当你只有很少的事务操作时,编程式事务管理通常比较合适。例如,如果你有一个Web应用,其中只有特定的更新操作有事务要求,你可能不愿使用Spring或其他技术设置事务代理。这种情况下,使用TransactionTemplate可能 是个好办法。

另一方面,如果你的应用中存在大量事务操作,那么声明式事务管理通常是值得的。它将事务管理与业务逻辑分离,而且在Spring中配置也不难。使用Spring,而不是EJB CMT,声明式事务管理在配置上的成本极大地降低了。

9.8. 与特定应用服务器集成

一般来说,Spring的事务抽象与应用服务器是无关的。另外,使用Spring的 JtaTransactionManager 类时,一种可选的方式是通过JNDI查询获得JTAUserTransactionTransactionManager 对象,其中后者可以被设置为自动探测,这时针对不同的应用服务器有不同的方式。能够直接访问TransactionManager,确实在很大程度上增强了事务语义,可以参考JtaTransactionManager 类的javadoc获得更多细节。

9.8.1. BEA WebLogic

在一个使用WebLogic 7.0、8.1或更高版本的环境中,你一般会优先选用特定于WebLogic的 WebLogicJtaTransactionManager 类来取代基础的JtaTransactionManager 类。在WebLogic环境中,该类提供了对Spring事务定义的完全支持,超过了标准的JTA语义。它的特性包括:支持事务名,支持为每个事务定义隔离级别,以及在任何环境下正确地恢复事务的能力。

9.8.2. IBM WebSphere

在WebSphere 5.1、5.0和4环境下,你可以使用Spring的 WebSphereTransactionManagerFactoryBean 类。这是一个工厂类,通过WebSphere的静态 访问方法获取到JTATransactionManager 实例。(这些静态方法在每个版本的WebSphere中都不同。)一旦通过工厂bean获取到JTATransactionManager 实例,就可以使用该实例装配一个Spring的JtaTransactionManager bean,它封装了JTA UserTransaction,提供增强的事务语义。请参考相关javadoc以获得完整信息。

9.9. 公共问题的解决方案

9.9.1. 对一个特定的 DataSource 使用错误的事务管理器

开发者需要按照需求仔细地选择正确的 PlatformTransactionManager 实现。理解Spring的事务抽象如何与JTA全局事务一起工作是非常重要的。使用得当,就不会有任何冲突:Spring仅仅提供一个直观的、可移植的抽象层。

如果你使用全局事务,你 必须 为你的所有事务操作使用Spring的 org.springframework.transaction.jta.JtaTransactionManager 类(或 特定于某种应用服务器的子类)。否则Spring将试图在象容器数据源这样的资源上执行局部事务。这样的局部事务没有任何意义,好的应用服务器会把这些情况视为错误。

9.10. 更多的资源

从下面的链接中你可以找到更多关于Spring框架事务支持方面的资源

  • 来自 InfoQ 的 Java事务设计策略(Java Transaction Design Strategies) 是一本介绍Java事务方面相当好的书。同时它也包含了如何在Spring框架和EJB3中配置与使用事务的对比示例。

文章摘录自:http://www.redsaga.com/spring_ref/2.0/html/transaction.html

原创粉丝点击