b spring 之事务管理框架 transaction

1.事务管理

想使用全面的事务管理是使用Spring Framework的原因。Spring Framework提供一个全面一致的事务管理机制,它带来以下好处。

  • 一致的编程模型,囊括不同的事务APIS,例如 JAVA Transaction API(JTA),JDBC,
    Hibernate,以及JAVA的持久层JPA

  • 支持声明式事务管理

  • 更为简化的API–针对事务管理API,比如JTA

  • 和Spring的 data access抽象良好的集成

下面部分描述了Spring框架事务的特点和技术

  • spring事务的优势是支持模型描述:你将会使用Spring Framework的事务抽象而不是 EJB 的事务(CMT)或者驱动本地的事务模型通过 属性化API,比如Hibernate。

  • 理解Spring 框架事务的抽象轮廓,通过核心的类和如何配置 和获取 数据源DataSource实例从多个source中

  • 对于事务所描述的需要同步的资源,应用应当如何来保证资源被创建,重用,和清理

  • 用声明式的方式来使用事务管理

  • 编程化的方式使用事务管理

  • 事务绑定了事件机制,你应当如何在事务中使用应用事件

1.1 Spring框架事务模型的优点

传统,java EE开发者有两种事务模式的选择:全局或者本地,两者都有深刻的局限。下面先会讲两种模型的限制,紧接着会说Spring的事务模型是如何处理这两种限制的。

1.1.1 全局事务

全局事务是让你在多种事务源下工作,典型的有关系型数据库和消息队列。应用服务端管理全局事务依赖(JTA),但是JTA的API很笨重。还有呢,一个JTA的UserTranscation一般需要JNDI源,意味着使用JTA必须要用JNDI。全局事务限制了任何潜在的代码重用,因为JTA只在应用的服务环境中可以获取。
以前,使用全局事务最好的方式是通过EJB的CMT(container Managed Transaction)。CMT是声明式事务管理。EJB CMT去除了对JNDI遍历的需求,虽然EJB本身也是需要使用JNDI的。它移除了最多但不是所有的java code去控制事务。CMT的缺点是是和JTA和应用服务端环境强绑定了。而且在EJB选择实现业务逻辑也是一次性的。总结一下EJB的负面是不足够优雅,特别是面临向声明式事务转换的情形时。

1.1.2 本地事务

本地事务的特点是是信息源是很明确,例如一个事务和一个JDBC连接关联。本地事务也许更简单易用,但是致命的缺点:不能再多个事务源中工作。例如,在编码JDBC的事务管理时,不能在一个全局的JTA事务内,不能确保在多事务源时的正确性(在多用应用用一个事务员的时候这个问题会没有)。另一个缺点是本地事务侵入到了编程模型中。

1.1.3 Spring框架一致的事务模型

Spring解决了本地事务和全局事务的缺点。他让应用的的开发者在任何环境中使用一致的编程模型。一次开发,处处使用。同时也支持声明式和编程式的事务模型管理。大多数的用户喜欢使用声明式的事务模型。
在编程式的事务管理中,开发者可以和Spring抽象的事务模型进行开发,他可以跑在任何具有同样事务模型的基础设施之下。在更为推荐的声明式模型下,开发者典型的仅需很少的代码和事务模型关联起来即可,也不要其依赖Spring框架的事务API或者其他框架事务API

你需要在应用server端去传统的事务管理?
Spring Framework的事务管理支持更改了有关企业Java应用程序何时需要应用服务器的传统规则。 特别是,您不需要纯粹用于通过EJB进行声明式事务的应用程序服务器。实际上,即使您的应用服务器具有强大的JTA功能,您也可能会决定,与EJB CMT相比,Spring Framework的声明式事务提供更多的功能和更高效的编程模型。 通常,仅当您的应用程序需要处理跨多个资源的事务时才需要应用程序服务器的JTA功能,而这并不是许多应用程序所必需的。许多高端应用程序使用单个高度可扩展的数据库(例如Oracle RAC)来代替。独立事务管理器(例如Atomikos Transactions和JOTM)是其他选择。当然,您可能需要其他应用程序服务器功能,例如Java消息服务(JMS)和Java EE连接器体系结构(JCA)。 Spring Framework使您可以选择何时将应用程序扩展到完全加载的应用程序服务器。如今,使用EJB CMT或JTA的唯一替代方法是用本地事务(例如JDBC连接上的事务)编写代码,如果您需要该代码在全局的,容器管理的事务中运行,则面临大量的返工。使用Spring Framework,仅需要更改配置文件中的某些Bean定义(而不是代码)。

1.2 理解Spring事务的抽象

理解Spring事务抽象的关键点是事务策略的概念。一个事务策略定义在org.springframework.transaction.PlatformTransactionManager接口中,如下所示
b spring 之事务管理框架 transaction
这是一个SPI接口,你可以编程式的使用它。因为PlatformTransactionManager是一个接口,可以被容器的mock和打桩。它不和查询策略进行绑定,例如JNDI。
PlatformTransactionManager的实现也像其他在Spring FrameWork Ioc 容器的对象一样。好处是可以是Spring的事务抽象足够的独立,即使你接入了JTA。你可以更轻易的使用JTA进行直接测试。
再强调一下,和Spring的哲学保持一致,TransactionException(事务异常)-被PlatformTransactionManager方法所跑出来的全是unchecked(因为,他们全部实现了java.lang.RuntimeException类)。事务基础设施的失败几乎是致命的,在很少的例子里,应用的代码可以在事务故障中恢复,应用开发者仍旧可以选择去处理和捕获事务异常(TransactionException)。

getTransaction(..)方法返回了一个TransactionStatus事务状态对象,依赖于一个TransactionDefinition事务定义参数。返回的TransactionStatus可以是一个新的事务,也可以是一个存在的事务(会在事务栈中搜索)。
下面的内容意指,在JavaEE事务上下文中,TransactionStatus和线程执行相关联。
这个TransactionDefinition接口具体

  • Propagation传递:典型的,所有的代码都在一个事务socpe中运行。当一个事务的上下文存在时,这时需要执行一个事务性的方法时,你可以具体指定这个方法行为。例如,代码可以继续自行已存在的事务,或者已存在的事务被拼接到一个新的事务。Spring提供的所有事务传递操作,都类似于EJB CMT操作。
  • Isolation(隔离):事务之前是互相隔离的。例如,难道事务可以看的见其他事务未提交的写操作吗。
  • Timeout:事务运行的最大时长,以及在事务基础设施下的回滚操作。
  • Read-only status:你可以使用read-only事务,当你的代码读但是不修改数据。Read-only事务在一些场景中很有用,比如你在使用Hibernate

这些设置反应了标准的事务概念。如果必要的话,还会引入对事务隔离级别的划分和其他事务概念。理解这些概念是使用Spring事务解决方案的关键。
这个TransactionStatus的接口提供了一个简单的方式去执行和查询事务状态。这个应该会很好理解,因为他们类似于通用的事务API。下列展示了TransactionStatus接口。
b spring 之事务管理框架 transaction
不管你是编程化还是声明式的事务管理,定义正确的PlatformTransactionManager实现都是必要的。典型的你可以通过依赖注入来实现这个定义。
PlatformTransactionManager的实现通常要求指导运行在那种环境之下(JDBC,JTA,Hibernate等)。下列的例子示范了在JDBC平台下定义一个本地的PlatformTransactionManager实现。
b spring 之事务管理框架 transaction
bean的定义
b spring 之事务管理框架 transaction
如果你使用了JTA,下面是如何通过JNDI来获取DataSource
b spring 之事务管理框架 transaction
JtaTransactionManager不需要知道具体的DataSource,因为他用了全局性的事务管理基础设施。
你也可以是用Hibernate的本地事务。在这个例子中你需要定义Hibernate的LocalSessionFactoryBean–你的应用不能获得Hibernate Session 实例。
这里的DataSource和前面展示本地JDBC例子是相似的,因此,就不在限免示例中展示了。
这个 txManagerbean的类型是
HibernateTransactionManager类型。DataSourceTransactionManager需要一个DataSource的引用。HibernateTransactionManager需要一个SessionFactory的引用
b spring 之事务管理框架 transaction
xml配置
b spring 之事务管理框架 transaction

1.3. 用事务来同步资源

如何创建事务管理器和将不同的源进行关联—需要创进行同步操作(例如DataSourceTramsactionManager对一个JDBC DataSource,HibernateTransactionManager对一个SessionFactory等),现在可以清空这个概念了。
本节描述了应用程序代码(通过使用诸如JDBC,Hibernate或JPA之类的持久性API直接或间接)确保如何正确创建,重用和清理这些资源。本节还讨论如何通过相关的PlatformTransactionManager(可选)触发事务同步。

1.3.1 高级的同步方式

High-level Synchronization Approach
最好的方式是使用Spring高级的模板,基于持久化的集成API或者利用ORM APIS(和transaction-aware工厂或者代理本地的资源工厂)。
这些transaction-aware解决方案内部靠处理资源的创建和重用,清除,可选的事务同步和异常路由。因此,使用 data access 代码可以不去处理这些任务,但是需要纯粹的关注持久层逻辑。一般来说,你使用本地的ORM API或者模板方案去使用JDBC,你可以使用JdbcTemplate。

1.3.2.低级的同步方式

Low-level Synchronization Approach

DataSourceUtils(JDBC)、EntityManagerFactoryUtils(JPA),SessionFactoryUtils(Hibernate)
等等都存在在lower level的API。当你想用应用代码去直接处理本地的持久化APIS时,你可以用这些类确保Spring框架 获取了这些实例,事务被同步,和异常被路由到统一的API上去。
Spirng Utils类处理JDBC
例如,JDBC(并非传统地在DataSource调用getConnection()方式),你可以使用org.springframework.jdbc.datasource.DataSourceUtils
b spring 之事务管理框架 transaction
如果存在事务有一个连接同步在其上,这个实例就会被返回。或者这个方法调用触发器去创建一个新的连接,他会和已存在地事务做同步,也确保同一个事务里,下一个重用会用到。需要注意的是,任何SQLException被Spring Framework的CannotGetJdbcConnectionException包裹,还会有一个DataAccessException类型。这种方式会在你遇到SQLException时提供更多的信息,也确保不同持久层类型的一致性反馈。
这种方式让你和Spring 事务做了解耦,以便与不用事务,来单独使用。

当然一旦你已经使用了Spring 地JDBC支持,JPA支持,或者 Hibernate支持,你会更偏向不用DataSourceUtils或者其他帮助类,因为你更喜欢通过Spring的抽象层来直接调用相关的API.
例如,如果你用了Spring地JdbcTemplate或者 jdbc.object
去简化你使用JDBC的代码,正确的连接connection会被获取,你不需要写任何特殊的代码。

1.3.3. TransactionAwareDataSourceProxy

在最底层地级别,都会存在TransactionAwareDataSourceProxy类。这个是DataSource的代理,可以封装目标DataSource去添加Spring事务管理。这方面和事务JNDI是相似的。
你几乎不会用到这个类。

1.4 声明式事务管理

使用AOP让声明式事务成为可能。然而传统的AOP代码在Spring中是以模板的形式存在,用这些模板不能有效的理解AOP的概念。
Spring声明式事务和EJB CMT的概念是相似的,你可以细化事务的行为到方法级别。你可以在事务context中调用setRollbackOnly()。但是和CMT存在一些不同,如下所示。

  • 不像EJB CMT和JTA绑定。Spring 框架的失误声明可以在任何环境中。它可以和JTA事务也可以和本地事务–jdbc,jpa或者Hibernate工作
  • 你也可以声明事务管理到任何类上,而不是在EJB上特殊类
  • spring提供rollback 规则,编程和声明都提供,EJB不会
  • spring可以让你使用AOP自定义事务行为,比如插入自定义行为到事务rollback。但是EJB中你不能影响容器的事务管理,除了setRollbackOnly()
  • spring框架不支持事务context远程调用,是一个高级end(包含很多组件)的server使用的,如果你需要这个特性,推荐使用EJB

注意rollback规则的概念是很重要的。它们让你可以去指定那些异常会触发自动回滚。你可以依靠声明和配置,但不是代码的形式配置。因此,虽然你可以调用 setRollbackOnly() 在TransactionStatus 对象上去回滚当前事务,但是更常用的是设定当出现MyApplicationException异常来回滚。最重要的特点是事务对象不依赖事务基础设施,例如你无需去导入spring事务API。
虽然EJB容器默认的行为是在系统异常后会回滚,但是EJB CMT不会在一个java异常上进行回滚。

1.4.1 理解Spring 声明式事务的实现。

仅仅让你给你的类加上@Transactional注解和在你的配置上加上@EnableTransactionManagement是不够的。还需要让你理解工作过程。
关键概念是spring声明事务的打开时通过 AOP PROXY和事务 advice是靠metadata驱动的。AOP和事务metaData 结合通过使用AOP代理:使用TransactionInterceptor和合适的PlatformTransactionManager实现结合来去驱动事务方法。
b spring 之事务管理框架 transaction

1.4.2 事务声明实现的样例

有两个类FooBar作为占位,你可关注事务的应用,而不是具体的业务行为。这个例子的目的是,面对DefaultFooService抛出UnsupportedOperationException异常时,让你看见事务是如何被创建和回滚的。
FooService
b spring 之事务管理框架 transaction
FooService的实现
b spring 之事务管理框架 transaction
假设类中两个get方法必须在事务是read-only语义的上下文执行,另外两个方法
insertFoo(Foo)和updateFoo(Foo)是在read-write语义下执行,请看下列的配置细节
看英文:指定FooService的特点(readonly等)
b spring 之事务管理框架 transaction
上面是类中<tx:advice>是指定方法的属性,其中transaction-manager属性指定PlatformTransactionManager是哪个,这个示例中,是txManager bean,在xml结尾有声明。
<aop:config>确保事务advice的定义被合适的切点执行。

  • 第一fooServiceOperation应该定义切点去匹配FooService的方法
  • 第二你应该使用advisor来和txAdvice关联
    结果是,命中fooServiceOperation后,txAdvice会执行。

<aop:pointcut>定义是aspectJ的用法,示例中只切了一个类,但是往往会切一个层,一堆类。写法如下
b spring 之事务管理框架 transaction
调用
前面的配置为fooService声明了一个代理,调用如下(用没有jdbc来触发异常)
b spring 之事务管理框架 transaction
打印
b spring 之事务管理框架 transaction

1.4.3. Rolling Back a Declarative Transaction

回滚一个声明式的事务。
上一部分描述了如何去细化事务设置的大体轮廓。这部分会描述如何以一个简单的声明模板来控制事务回滚。
推荐的方式是指明那个方法抛出异常时会回滚。spring会依据未处理异常来来标记该事务是否回滚。
spring默认只有在运行时抛出unchecked异常才会回滚。就是当抛出一个RuntimeException(Error也可以)会导致回滚。Checked异常不会导致回滚。
你可以配置具体哪种会导致回滚,下列是
配置一个checked异常导致回滚
b spring 之事务管理框架 transaction
下列是不让某种异常触发回滚
b spring 之事务管理框架 transaction
条件冲突时强类型获胜,如下是insXXXX获胜
b spring 之事务管理框架 transaction
编程化的配置
b spring 之事务管理框架 transaction

1.4.4. Configuring Different Transactional Semantics for Different Beans

为不同的bean配置不同的事务语义。
当有很多service layer的对象时,而且想在一个配置下为不同对象配置不同的语义。你可以用<aop:advisor>元素来区分不同的pointcut和advice-ref。
假定你第一个layer都是在x.y.service包下。去让所有的类都以Service为后缀或者子包。编写的形式如下
这个是两个service的配置,但是一个pointcut
b spring 之事务管理框架 transaction
两个service,两个pointcut
b spring 之事务管理框架 transaction

1.4.5. <tx:advice/> Settings

这部分会总结一下多样的事务配置,你可以具体用<tx:advice/>。

  • 这个propagation setting 是REQUIRED
  • 这个封装的基本是DEFAULT
  • 事务的是 read-write
  • 事务的耗时默认是事务系统配置的,或者不支持
  • 任何RuntimeException的触发器回滚,任何checked Exception

The following table summarizes the various attributes of the tx:method/ tags that are nested within tx:advice/ and tx:attributes/ tags:

b spring 之事务管理框架 transaction

1.4.6 Using @Transactional

使用事务注解,除了使用基于xml的配置,你还可以使用注解。声明式的事务让声明更贴近代码处。
使用@Transactional
b spring 之事务管理框架 transaction
类级别的声明,会让类下所有的方法都拥有该事务主注解,但不会应用到其父类。所以子类需要重新声明该注解才会有效。
除此之外你需要在Configuration上使用@EnableTransactionManagement打开注解才行。
以上的行为等同于如下xml配置。
b spring 之事务管理框架 transaction
事务驱动的注解配置
b spring 之事务管理框架 transaction
你可以把@Transactional放置在interface,interface中的方法,类中的public方法。但是很少的场景会用到事务注解,所以使用<tx:annotation-driven/>来打开和关闭事务行为。
注意代理模式下(一直就是),只有外部的方法调用才会被拦截,自调用不会产生作用
事务注解指定参数。

@Transactional Setting

b spring 之事务管理框架 transaction
默认的@Transactional如下

  • propagation 属性 PROPAGATION_REQUIRED.
  • isolation level 是 ISOLATION_DEFAULT.
  • The transaction 是 read-write.
  • 超时时间是事务系统,或者没有。The transaction timeout defaults to the default timeout of the underlying transaction system, or to none if timeouts are not supported.
  • 任何RuntimeException会触发回滚 Any RuntimeException triggers rollback, and any checked Exception does not.
    事务属性详细配置
    b spring 之事务管理框架 transaction
    现在你可以用事务的name来精准的控制事务了。对于声明式事务,事务name是fully-qualified class name+ method name。

Multiple Transaction Managers with @Transactional 多事务管理

大多数Spring应用只有一个单独的事务管理,但是也有多个书屋管理的抢矿。你可以使用@Trancsation 的value属性来单独标识PlatformTransactionManager。这里可以用bean的name和qualifier value来标识。
b spring 之事务管理框架 transaction
这个例子中这两个方法跑在单独的事务管理中

自定义快捷注解Custom Shortcut Annotations

利用自定义注解可以简化注解配置
b spring 之事务管理框架 transaction

1.4.7 事务传递

注意这章节是讲解一些通用的事务传递,需要理解事务传递的行为。

understand PROPAGATION_REQUIRED

b spring 之事务管理框架 transaction
这个注解会强制使用一个物理上的事务,没有事务时用一个物理事务,有事务时加入一个已经存在的外部事务。在单线程中是最好用的。
当传递属性设置成PROPAGATION_REQUIRED时,一个逻辑的事务Scope会为没哟个方法设置。每一个逻辑事务scope可以独立的决定回滚状态,内部scope与外部scope逻辑上时独立的。在PROPAGATION_REQUIRED下,所有的scope会映射mapped到一个相同的物理事务。所以rollbakc-only标记的设置(inner事务)会影响到(outter事务)。
然而,一旦内部事务设置了rollback-only 标记,外部事务不能决定rollback,所以这个rollback是不希望发生的。一个UnexpectedRollbackException异常就抛出了。所以外部的事务需要明确的指明接受这个异常,才能继续提交

PROPAGATION_REQUIRES_NEW

b spring 之事务管理框架 transaction
PROPAGATION_REQUIRES_NEW总是会用一个独立的物理事务,永远不会加入outter事务。所以提交和回滚都是独立的。当内部事务执行时,外部事务会暂时暂停。这里的独立可以声明独立的等级。内部事务不会继承外部事务的timeout和read-only属性。

PROPAGATION_NESTED

事务嵌套使用了一个物理事务通过多个事务savepoints。类如分区回滚让一个事务可以在他自己的scope内回滚,外部的事务可以继续执行。这种事务配置典型的应用在JDBC的savepoints。

1.4.8 Advising Transactional Operations

事务操作advice。假如你要执行事务Operations和基础的profiling advice。你该如何配置<tx:annotation-driven/>。如前面的离职,你调用updateFoo(Foo)方法,你想去看如下的行为。

  • The configured profiling aspect starts.
  • The transactional advice executes.
  • The method on the advised object executes.
  • The transaction commits.
  • The profiling aspect reports the exact duration of the whole transactional method invocation. 精确的时间耗时

b spring 之事务管理框架 transaction
注意advice的顺序是靠order接口设定,以下是advice的如何引入的示例。
下面是配置一个FooService的bean拥有profilling和事务aspect,按照指定的顺序。
你可以指定任意数量的aspect,仿照类似的模板。
b spring 之事务管理框架 transaction
下面的例子中是纯xml的事务配置,可以看见<aop:config>中配置了两个不同的aspect,一个是advisor一个是aspect,两者一个意思,aspect偏向aspecj中的概念,advisor是Spring中类似aspect的类。
b spring 之事务管理框架 transaction

1.4.9 Using @Transactional with AspectJ

和AspectJ一起使用,不详细叙述。

1.5 编程化的事务管理

通过使用

  • TransactionTemplate
  • 直接使用PlatformTransactionManager的实现

1.5.1 使用TransactionTemplate

同其他template(JDBCTemplate)类似,TransactionTemplate是一样的使用方式。它利用模板让使用者更关注于业务。
应用代码必须在事务context中执行,并且严格用TransactionTemplate类似于下面的例子。作为应用的开发者可以实现TransactionCallback来包含需要执行的代码,你可以通过实现Transbackcallback的execute()方法。

b spring 之事务管理框架 transaction
没有返回值时可以使用TransactionCallbackWithoutResult
b spring 之事务管理框架 transaction

Specifying Transaction Settings

例如设置属性
b spring 之事务管理框架 transaction

TransactionTemplate是线程安全的,TransactionTemplate中不会包含过度状态,定义好一个之后可以在注入到不同的Bean中达到共用一个的目的

1.5.2 使用 PlatformTransactionManager

也可以使用org.springframework.transaction.PlatformTransactionManager来直接管理你的事务。
通过使用TransactionDefinitionTransactionStatus来进行初始化事务,回滚,和提交
b spring 之事务管理框架 transaction

1.6 在编程化事务和声明式事务之前的选择

编程化事务使用于你有清晰的规划和小量的事务操作。
相反有大量的事务操作,声明式事务更划算

1.7 Transaction-bound Events

事务绑定事件。4.2以后,事件的监听器可以被绑定到事务的短语上去。典型的是事务成功执行后触发一个事件。这么做让当前事务对外输出信息更灵活。
你可以注册一个寻常的监听器@EventListener。如果你需要绑定他到事务上,请使用@TransactionEventListener。当你这么做时,listener会被绑定到commit语义上(默认)。
假定一个compoent发送了一个顺序串创建的事件,并且我们想去定义一个监听器来处理这个时间当事务commit成功时
b spring 之事务管理框架 transaction
注意这里区分不同事务是靠参数的事件类型区分的。
@TransactionalEventListener注解暴露一个短语属性来让你配置发生的位置。
BEFORE_COMMIT, AFTER_COMMIT (default), AFTER_ROLLBACK, AFTER_COMPLETION

1.8应用server的特殊集成

Spring’s JtaTransactionManager 对 Java EE application servers 是一个标准的选择,可以自动侦测和加载。这里的标准,不是说通用,或推荐,是对现有的jta事务的一种支持。

1.8.1 IBM WebSphere

WebLogicJtaTransactionManager

1.9 常见问题

1.9.1. Using the Wrong Transaction Manager for a Specific DataSource

Use the correct PlatformTransactionManager implementation based on your choice of transactional technologies and requirements. Used properly, the Spring Framework merely provides a straightforward and portable abstraction. If you use global transactions, you must use the org.springframework.transaction.jta.JtaTransactionManager class (or an application server-specific subclass of it) for all your transactional operations. Otherwise, the transaction infrastructure tries to perform local transactions on such resources as container DataSource instances. Such local transactions do not make sense, and a good application server treats them as errors.

1.10. Further Resources

看原文吧
原文联接