Spring提供了一致的事务管理抽象。这个抽象是Spring最重要的抽象之一, 提供如下的优点:
传统上,J2EE开发者有两个事务管理的选择: 全局或 局部事务。全局事务由应用服务器管理,使用JTA。局部 事务是资源相关的:例如,一个和JDBC连接关联的事务。这个选择有深刻的含义。 全局事务提供和多个参与事务的资源(需要指出的是多数应用使用单一参与事务 的资源)。使用局部事务,应用服务器不需要参与事务管理,并且不能帮助确保 跨越多个资源的正确性。
全局事务有一个显著的不利方面,代码需要使用JTA:一个笨重的API(部分是 因为它的异常模型)。此外,JTA UserTransaction通常需 要从JNDI获得,意味着我需要同时使用JNDI和JTA来使用 JTA。显然全部使用全局事务限制了应用代码的重用性,因为JTA通常只在应用服 务器的环境中才能使用。
首选的使用全局事务的方式是通过EJB的CMT (容器管理的事务): 一种形式的 声明式事务管理(区别于编程式事务管理 )。EJB的CMT消除了事务相关的JNDI查找的需求,虽然使用EJB本身 肯定需要使用JNDI。它消除大多数――不是全部――书写Java代码控制事务的需求。 显著的不利方面是CMT和JTA以及应用服务器环境捆绑在一起,并且只有我们选择 使用EJB实现业务逻辑时才能使用,或者至少使用在一个事务化EJB的外观 (Fa?ade)后。EJB有如此多的诟病,当存在可供选择的声明式事务管理时, EJB不是一个吸引人的建议。
局部事务容易使用,但也有明显的不利方面:它们不能跨越多个参与事务的资 源使用,并且趋向侵入的编程模型。例如,使用JDBC连接事务管理的代码不能使 在全局的JTA事务中。
Spring解决了这些问题。它使应用开发者能够使用在任何环境 下使用一致的编程模型。你可以只写一次你的代码,这些代码可以从在不同环境 下的不同事务管理策略中获益。Spring同时提供声明式和编程式事务管理。
使用编程式事务管理,开发者使用Spring事务抽象,这个抽象可以使用的任何 底层事务基础之上。使用首选的声明式模型,开发者通常书写很少的事务相关代 码,因此不依赖Spring或任何其他事务API。
Spring提供两种方式的编程式事务管理
我们通常推荐使用第一种方式。
第二种方式类似使用JTA UserTransaction API (虽然异常处理少一点麻烦)。
Spring也提供了声明式事务管理。这是通过Spring AOP实现的。
从考虑EJB CMT和Spring声明式事务管理的相似以及不同之处出发是很有益的。 基本方法是一致的:都可以指定事务管理到单独的方法;如果需要可以在事务上 下文调用setRollbackOnly()方法。不同之处:
-
不同于EJB CMT绑定在JTA上,Spring声明式事务管理可以在任何环境下使用。 只需更改配置文件,它就可以和JDBC、JDO、Hibernate或其他的事务机制一起工作
-
Spring可以使声明式事务管理应用到POJO,不仅仅是特定的类,如EJB
-
Spring提供声明式回滚规则:EJB没有对应的特性, 我们将在下面讨论这个特性。回滚可以声明式控制,不仅仅是编程式的
-
Spring通过AOP提供定制事务行为的机会。例如,如果需要,你可以在事务 回滚中插入定制的行为。你也可以增加事务通知一样增加任意的通知。使用 EJB CMT,除了使用setRollbackOnly()没有其他的办法能 够影响容器的事务管理
-
Spring不提供高端应用服务器提供的跨越远程调用的事务上下文传播。如 果你需要这些特性,我们推荐你使用EJB。然而,不要过度使用这些特性。通常我 们并不希望事务跨越远程调用
回滚规则的概念是很重要的:它们使得我们可以指定那些异常应该发起一个自 动回滚。我们在配置文件中而不是Java代码中指定这些声明。因此,虽然我们仍 然可以编程式调用TransactionStatus对象的 setRollbackOnly()方法来回滚当前事务,多数时候我们可以 指定规则如MyApplicationException应该用于导致一个回滚。 这有显著的优点,业务对象不需要依赖事务基础设施。例如,他们通常不需要引 入任何Spring API,事务或其他任何东西。
EJB的默认行为是遇到系统异常(通常是运行时异常) EJB容器自动回滚事务。EJB CMT遇到应用程序异常 (除了java.rmi.RemoteException外被检查的异常)时不 会自动回滚事务。虽然Spring声明式事务管理沿用EJB CMT约定(遇到不被检查的 异常自动回滚事务),但是这是可以定制的。
按照我们的基准,Spring声明式事务管理的性能要胜过EJB CMT。
通常设置Spring事务代理的方法是通过TransactionProxyFactoryBean。我们需 要一个包装在事务代理中目标对象。这个目标对象一般是一个POJO的bean。当我 们定义TransactionProxyFactoryBean时,必须提供一个相关的 PlatformTransactionManager的引用和事务属性。 事务属性含有上面描述的事务定义。
<bean id="petStore"
class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean">
<property name="transactionManager"><ref bean="transactionManager"/></property>
<property name="target"><ref bean="petStoreTarget"/></property>
<property name="transactionAttributes">
<props>
<prop key="insert*">PROPAGATION_REQUIRED,-MyCheckedException</prop>
<prop key="update*">PROPAGATION_REQUIRED</prop>
<prop key="*">PROPAGATION_REQUIRED,readOnly</prop>
</props>
</property>
</bean>
事务代理会实现目标对象的接口:这里是id为petStoreTarget.的bean(使用 CGLIB可以实现具体类的代理,只要设置proxyTargetClass属性为true就可以。这 将自动设置,如果目标对象没有实现任何接口。通常,我们希望面向接口而不是 类编程)。可以(一般来说是好的想法)限定事务代理使用proxyInterfaces来代 理指定的接口。也可以通过继承至 org.springframework.aop.framework.ProxyConfig几个属 性来定制TransactionProxyFactoryBean的行为,并在所有的AOP代理工厂中共享。
这里的transactionAttributes通过定在 org.springframework.transaction.interceptor.NameMatchTransactionAttributeSource 类中的属性格式来设置。这个包括通配符的方法名称映射是很直观的。注意 insert*的映射的值包括回滚规则。添加-MyCheckedException 指定如果方法抛出MyCheckedException或它的子类,事务将 会自动回滚。可以指定个回滚规则,用逗号分隔。-前缀强制回滚,+前缀指定提 交(这允许即使抛出未被检查的异常时也可以提交事务,当然你自己要明白自己 在做什么)。
TransactionProxyFactoryBean允许你通过 “preInterceptors”和“postInterceptors”属性设置“前”或“后”通知来提供额外的 拦截行为。可以设置任意数量的“前”和“后”通知,它们的类型可以是 Advisor(可以包含一个pointcut), MethodInterceptor或被当前Spring配置支持的通知类型 (例如ThrowAdvice, AfterReturningtAdvice或BeforeAdvice, 这些都是默认支持的)。这些通知必须支持实例共享模式。如果你需要高级AOP特 性来使用事务,如有状态的混入,那最好使用通用的 org.springframework.aop.framework.ProxyFactoryBean, 而不是TransactionProxyFactoryBean代理创建者。
也可以设置自动代理:配置AOP框架,不需要单独的代理定义类就可以生成类的 代理。
更多信息和实例请参阅AOP章节。
如果你只有很少的事务操作,使用编程式事务管理是很好的主意。例如, 如果你有一个WEB应用需要为某些更新操作提供事务,你可能不想用Spring或其 他技术设置一个事务代理。使用 TransactionTemplate可能是个很好的方法。
另一方面,如果你的应用有大量的事务操作,编程式(译注:应为声明式, 疑是作者笔误)事务管理通常是很有价值的。它使得事务管理从业务逻辑分离, 并且Spring中配置也不困难。使用Spring, 而不是EJB CMT,声明式事务管理配置的成本极大地降低。
Spring的事务管理能力――尤其声明式事务管理极大地改变了J2EE应用程序需要 应用服务器的传统想法。
特别地,你不需要应用服务器仅仅为了通过EJB声明事务。事实上,即使你拥 有强大JTA支持的应用服务器,你也可以决定使用Spring声明式事务管理提供比 EJB CMT更强大更高效的编程模型。
只有需要支持多个事务资源时你才需要应用服务器的JTA支持。许多应用没有 这个需求。例如许多高端应用使用单一的高度可升级的数据库,如Oracle 9i RAC。
当然也许你需要其他应用服务器的功能,如JMS和JCA。但是如果你只需使用 JTA,你可以考虑开源的JTA实现,如JOTM(Spring整合了JOTM)。但是, 2004年早期,高端的应用服务器提供更健壮的XA资源支持。
最重要一点,使用Spring,你可以选择何时将你的应用迁移到完整 应用服务器。除了使用EJB CMT和JTA只能书写代码使用局部事务, 例如JDBC连接的事务,而且如果曾经需要全局的容器管理的事务,还面临着繁重 的改写过程,这些日子一去不复返了。使用Spring只有配置需要改变,你的代码 不需要。
开发着需要按照需求仔细的使用正确的 PlatformTransactionManager实现。
理解Spring事务抽象时如何和JTA全局事务一起工作是非常重要的。使用得当, 没有任何冲突:Spring仅仅提供一个简单的,可以移植的抽象层。
如果你使用全局事务,你必须为你的所有事务操作使用Spring的 org.springframework.transaction.jta.JtaTransactionManager。 否则Spring将试图在资源如容器数据源执行局部事务。这样的局部事务没有任何 意义,好的应用服务器会把这作为一个错误。