Transaction management 事务管理

来源:百度文库 编辑:神马文学网 时间:2024/03/29 10:19:46
9.1. Introduction 简介
One of the most compelling reasons to use Spring is the transaction support. Spring provides a consistent abstraction for transaction management that delivers the following benefits: 一个使用Spring的引人注目的因素是事务支持。 Spring提供了一致的事务管理抽象,这带来了以下好处:
Provides a consistent programming model across different transaction APIs such as JTA, JDBC, Hibernate, JPA, and JDO. 为不同的事务API提供了一致的编程模型,如JTA、JDBC、Hibernate、JPA和JDO
Supports declarative transaction management. 支持声明式事务管理
Provides a simpler, easier to use, API for programmatic transaction management than a number of disparate transaction APIs such as JTA. 提供比大多数事务API如JTA更简单的,易于使用的编程式事务管理API
Integrates with Spring‘s various data access abstractions. 整合Spring的各种数据访问抽象
This chapter is divided up into a number of sections; these sections are described below, along with a link so that you can navigate directly to the section which interests you. 这章被分成几个小节,每节的描述和链接列在下面,你可以直接跳到感兴趣的部分。
The first section, entitledMotivations, describes why one would want to use Spring‘s transaction abstraction as opposed to EJB CMT or driving transactions via a proprietary API such as Hibernate. 第一节,动机,描述为何 你愿意使用Spring的事务抽象,而不是EJB CMT或者一个私有的API,比如Hibernate的事务处理。
The second section, entitledKey abstractions outlines the core classes in Spring‘s transaction support, as well as how to configure and obtain DataSource instances from a variety of sources. 第二节,关键抽象, 列举了Spring事务支持的核心类,以及如何从多种数据源去配置并获得一个 DataSource实例。
The third section, entitledDeclarative transaction management covers, Spring‘s support for declarative transaction management. 第三节,声明式事务管理, 讲述了Spring如何支持声明式事务管理。
The fourth section, entitledProgrammatic transaction management covers, Spring‘s support for programmatic (i.e. explicitly coded) transaction management. 第四节,编程式事务管理, 介绍了Spring如何支持编程式(即书写代码)事务管理。
The chapter closes up with some discussion of best practices surrounding transaction management (for example, choosing between declarative and programmatic transaction management). 本章末尾讨论了一些关于事务管理的最佳实践(比如,如何在编程式和声明式事务管理之间做选择)。
9.2. Motivations 动机
Is an application server needed for transaction management? 事务管理需要应用服务器吗?
Spring‘s transaction management support significantly changes traditional thinking as to when a J2EE application requires an application server. Spring的事务管理支持极大地改变了传统上认为J2EE应用需要应用服务器的认识。
In particular, you don‘t need an application server just to have declarative transactions via EJB. In fact, even if you have an application server with powerful JTA capabilities, you may well decide that Spring‘s declarative transactions offer more power and a much more productive programming model than EJB CMT. 尤其,你不需要仅仅为了通过EJB来使用声明式事务而使用应用服务器。事实上,即使你的应用服务器拥有强大的JTA功能, 你也有充分的理由可以发现,Spring的声明式事务比EJB CMT提供了更加强大、高效的编程模型。
You need an application server‘s JTA capability only if you need to enlist multiple transactional resources, and for many applications being able to handle transactions across multiple resources isn‘t a requirement. For example, many high-end applications use a single, highly scalable database (such as Oracle 9i RAC). 一般来说,只有当你需要支持多个事务性资源时,你才需要应用服务器的JTA功能。而大多数应用并不需要能够处理跨越多种资源。 例如,许多高端应用使用单一的、高伸缩性的数据库,如Oracle 9i RAC。
Of course you may need other application server capabilities such as JMS and JCA. 当然,也许你需要应用服务器的其他功能,比如JMS或JCA。
The most important point is that with Spring you can choose when to scale your application up to a full-blown application server. Gone are the days when the only alternative to using EJB CMT or JTA was to write code using local transactions such as those on JDBC connections, and face a hefty rework if you ever needed that code to run within global, container-managed transactions. With Spring, only configuration needs to change so that your code doesn‘t have to. 最重要的一点是,使用Spring,你可以选择何时把你的应用迁移到全功能的应用服务器。 使用EJB CMT或JTA都必须书写代码使用局部事务,例如JDBC连接,如果以前需要全局的容器管理的事务, 还要面临着繁重的改写代码的过程,这些日子一去不复返了。 使用Spring,你仅需要改动配置文件,而不必改动你的代码。
Traditionally, J2EE developers have had two choices for transaction management: global or local transactions. Global transactions are managed by the application server, using the Java Transaction API (JTA). Local transactions are resource-specific: the most common example would be a transaction associated with a JDBC connection. This choice has profound implications. For instance, global transactions provide the ability to work with multiple transactional resources (typically relational databases and message queues). With local transactions, the application server is not involved in transaction management and cannot help ensure correctness across multiple resources. (It is worth noting that most applications use a single transaction resource.) 传统上,J2EE开发者有两个事务管理的选择: 全局或 局部事务。全局事务由应用服务器管理,使用JTA。局部事务是和资源相关的:例如, 一个和JDBC连接关联的事务。这个选择有深刻的含义。例如,全局事务可以用于多个事务性的资源 (典型例子是关系数据库和消息队列)。使用局部事务,应用服务器不需要参与事务管理,并且不 能帮助确保跨越多个资源(需要指出的是多数应用使用单一事务性的资源)的事务的正确性。
Global Transactions 全局事务. Global transactions have a significant downside, in that code needs to use JTA, and JTA is a cumbersome API to use (partly due to its exception model). Furthermore, a JTA UserTransaction normally needs to be sourced from JNDI: meaning that we need to use both JNDI and JTA to use JTA. Obviously all use of global transactions limits the reusability of application code, as JTA is normally only available in an application server environment. 全局事务有一个重大的缺陷,代码需要使用JTA:一个笨重的API(部分是因为它的异常模型)。 此外,JTA的UserTransaction通常需要从JNDI获得,这意味着 我们为了JTA,需要 同时 使用JNDI 和 JTA。 显然全部使用全局事务限制了应用代码的重用性,因为JTA通常只在应用服务器的环境中才能使用。 Previously, the preferred way to use global transactions was via EJB CMT (Container Managed Transaction): CMT is a form of declarative transaction management (as distinguished from programmatic transaction management). EJB CMT removes the need for transaction-related JNDI lookups - although of course the use of EJB itself necessitates the use of JNDI. It removes most of the need (although not entirely) to write Java code to control transactions. The significant downside is that CMT is tied to JTA and an application server environment. Also, it is only available if one chooses to implement business logic in EJBs, or at least behind a transactional EJB facade. The negatives around EJB in general are so great that this is not an attractive proposition, especially in the face of compelling alternatives for declarative transaction management. 以前,使用全局事务的首选方式是通过EJB的 CMT(容器管理事务): CMT是声明式事务管理的一种形式(区别于编程式事务管理)。 EJB的CMT不需要任何和事务相关的JNDI查找,虽然使用EJB本身肯定需要使用JNDI。它消除大多数(不是全部)书写Java代码去控制事务。 重大的缺陷是CMT绑定在JTA和应用服务器环境上,并且只有我们选择使用EJB实现业务逻辑,或者至少处于一个事务化EJB的外观(Facade) 后才能使用它。EJB有如此多的诟病,尤其是存在其它声明式事务管理时,EJB不是一个吸引人的建议。
Local Transactions 局部事务. Local transactions may be easier to use, but have significant disadvantages: they cannot work across multiple transactional resources. For example, code that manages transactions using a JDBC connection cannot run within a global JTA transaction. Another downside is that local transactions tend to be invasive of the programming model. 局部事务容易使用,但也有明显的缺点:它们不能用于多个事务性资源。 例如,使用JDBC连接事务管理的代码不能用于全局的JTA事务中。 另一个缺点是局部事务趋向于入侵式的编程模型
Spring resolves these problems. It enables application developers to use a consistent programming model in any environment. You write your code once, and it can benefit from different transaction management strategies in different environments. Spring provides both declarative and programmatic transaction management. Declarative transaction management is preferred by most users, and is recommended in most cases. Spring解决了这些问题。它使应用开发者能够使用在任何环境 下使用一致的编程模型。你可以只写一次你的代码,这在不同环境 下的不同事务管理策略中很有益处。Spring同时提供声明式和编程式事务管理。 声明事务管理是多数使用者的首选,在多数情况下是被推荐使用的。
With programmatic transaction management, developers work with the Spring transaction abstraction, which can run over any underlying transaction infrastructure. With the preferred declarative model, developers typically write little or no code related to transaction management, and hence don‘t depend on Spring‘s transaction API (or indeed on any other transaction API). 使用编程式事务管理,开发者直接使用Spring事务抽象,这个抽象可以使用在任何 底层事务基础之上。使用首选的声明式模型,开发者通常书写很少的或没有与事务 相关的代码,因此不依赖Spring或任何其他事务API。
9.3. Key abstractions 关键抽象
The key to the Spring transaction abstraction is the notion of a transaction strategy. A transaction strategy is defined by the org.springframework.transaction.PlatformTransactionManager interface, shown below: Spring事务抽象的关键是事务策略的概念。这个概念由 org.springframework.transaction.PlatformTransactionManager 接口定义,如下:
public interface PlatformTransactionManager {TransactionStatus getTransaction(TransactionDefinition definition)throws TransactionException;void commit(TransactionStatus status) throws TransactionException;void rollback(TransactionStatus status) throws TransactionException;}This is primarily an SPI interface, although it can be usedprogrammatically. Note that in keeping with Spring‘s philosophy, PlatformTransactionManager is an interface, and can thus be easily mocked or stubbed as necessary. Nor is it tied to a lookup strategy such as JNDI: PlatformTransactionManager implementations are defined like any other object (or bean) in a Spring IoC container. This benefit alone makes it a worthwhile abstraction even when working with JTA: transactional code can be tested much more easily than if it used JTA directly. 这首先是一个SPI接口,虽然它也可以在 编码中使用。注意按照Spring的哲学,PlatformTransactionManager 是一个接口。因而如果需要它可以很容易地被模拟和 桩化。它也没有和一个查找策略如JNDI捆绑在一起:PlatformTransactionManager 的实现定义和其他Spring IoC容器中的对象一样。这个好处使得即使使用JTA,也 是一个很有价值的抽象:事务代码可以比直接使用JTA更加容易测试。
Again in keeping with Spring‘s philosophy, the TransactionException that can be thrown by any of the PlatformTransactionManager interface‘s methods is unchecked (i.e. it extends the java.lang.RuntimeException class). Transaction infrastructure failures are almost invariably fatal. In rare cases where application code can actually recover from a transaction failure, the application developer can still choose to catch and handle TransactionException. The salient point is that developers are not forced to do so. 继续Spring哲学,可由任何PlatformTransactionManager的接口方法抛出的 TransactionException是unchecked(继承自 java.lang.RuntimeException)的。 底层的事务失败几乎总是致命的。很少情况下应用程序代码可以从它们 中恢复,不过应用开发者依然可以捕获并处理 TransactionException,他们可以自由决定怎么干。
The getTransaction()method returns a TransactionStatus object, depending on a TransactionDefinition parameter. The returned TransactionStatus might represent a new or existing transaction (if there were a matching transaction in the current call stack - with the implication being that (as with J2EE transaction contexts) a TransactionStatus is associated with a thread of execution). getTransaction()方法根据一个类型为 TransactionDefinition的参数返回一个 TransactionStatus对象。返回的 TransactionStatus对象可能代表一个新的或已经存在的事 务(如果在当前调用堆栈有一个符合条件的事务。如同J2EE事务环境,一个 TransactionStatus也是和 执行线程关联的)。
The TransactionDefinition interface specifies: TransactionDefinition接口指定:
Isolation: the degree of isolation this transaction has from the work of other transactions. For example, can this transaction see uncommitted writes from other transactions? 事务隔离:当前事务和其它事务的隔离的程度。 例如,这个事务能否看到其他事务未提交的写数据?
Propagation: normally all code executed within a transaction scope will run in that transaction. However, there are several options specifying behavior if a transactional method is executed when a transaction context already exists: for example, simply running in the existing transaction (the most common case); or suspending the existing transaction and creating a new transaction. Spring offers all of the transaction propagation options familiar from EJB CMT. 事务传播:通常在一个事务中执行的所有代码都会在这个事务中运行。 但是,如果一个事务上下文已经存在,有几个选项可以指定一个事务性方法的执行行为: 例如,简单地在现有的事务中运行(大多数情况);或者挂起现有事务,创建一个新的事务。 Spring提供EJB CMT中常见的事务传播选项。
Timeout: how long this transaction may run before timing out (and automatically being rolled back by the underlying transaction infrastructure). 事务超时: 事务在超时前能运行多久(自动被底层的事务基础设施回滚)。
Read-only status: a read-only transaction does not modify any data. Read-only transactions can be a useful optimization in some cases (such as when using Hibernate). 只读状态: 只读事务不修改任何数据。 只读事务在某些情况下(例如当使用Hibernate时),是一种非常有用的优化。
These settings reflect standard transactional concepts. If necessary, please refer to a resource discussing transaction isolation levels and other core transaction concepts: understanding such core concepts is essential to using Spring or indeed any other transaction management solution. 这些设置反映了标准概念。如果需要,请查阅讨论事务隔离层次和其他核心事务概念的资源: 理解这些概念在使用Spring和其他事务管理解决方案时是非常关键的。
The TransactionStatus interface provides a simple way for transactional code to control transaction execution and query transaction status. The concepts should be familiar, as they are common to all transaction APIs: TransactionStatus 接口为处理事务的代码提供一个简单的控制事务执行和查询事务状态的方法。 这个概念应该是熟悉的,因为它们在所有的事务API中是相同的:
public interface TransactionStatus {boolean isNewTransaction();void setRollbackOnly();boolean isRollbackOnly();}Regardless of whether you opt for declarative or programmatic transaction management in Spring, defining the correct PlatformTransactionManager implementation is absolutely essential. In good Spring fashion, this important definition typically is made using IoC. 使用Spring时,无论你选择编程式还是声明式的事务管理,定义一个正确的 PlatformTransactionManager 实现都是至关重要的。 按照Spring的风格,这种重要定义都是通过IoC实现的。
PlatformTransactionManager implementations normally require knowledge of the environment in which they work: JDBC, JTA, Hibernate, etc The following examples from the dataAccessContext-local.xml file from Spring‘s jPetStore sample application show how a local PlatformTransactionManager implementation can be defined. (This will work with plain JDBC.) 一般来说,选择PlatformTransactionManager实现时需要知道 当前的工作环境,如JDBC、JTA、Hibernate等。下面的例子来自Spring范例应用—— jPetStore——中的dataAccessContext-local.xml文件,其中 展示了一个局部PlatformTransactionManager实现是怎么定义 的(仅限于纯粹JDBC环境)
We must define a JDBC DataSource, and then use the Spring DataSourceTransactionManager, giving it a reference to the DataSource. 我们必须先定义一个JDBC DataSource,然后使用Spring DataSourceTransactionManager,并传入指向 DataSource的引用。
The related PlatformTransactionManager bean definition will look like this: PlatformTransactionManager bean的定义如下:
If we use JTA, as in the dataAccessContext-jta.xml file from the same sample application, we need to use a container DataSource, obtained via JNDI, and a JtaTransactionManager implementation. The JtaTransactionManager doesn‘t need to know about the DataSource, or any other specific resources, as it will use the container‘s global transaction management infrastructure. 如果我们使用JTA,就像范例中dataAccessContext-jta.xml文件所示, 我们将从JNDI中获取一个容器管理的DataSource,以及一个 JtaTransactionManager实现。 JtaTransactionManager不需要知道 DataSource和其他特定的资源,因为它将使用容器提供的全局事务管理。
Note
The above definition of the ‘dataSource‘ bean uses the ‘jndi-lookup‘ tag from the ‘jee‘ namespace. For more information on schema-based configuration, seeAppendix A, XML Schema-based configuration, and for more information on the tags see the section entitledSection A.2.3, “The jee schema”. 上面‘dataSource‘ bean的定义使用了‘jee‘ 名称空间下的‘jndi-lookup‘标签。想了解更多的配置信息, 请看Appendix A, XML Schema-based configuration, 关于标签的信息,可参考Section A.2.3, “The jee schema”节。
We can use Hibernate local transactions easily, as shown in the following examples from the Spring PetClinic sample application. In this case, we need to define a Hibernate LocalSessionFactoryBean, which application code will use to obtain Hibernate Session instances. 我们可以很容易地使用Hibernate局部事务,就像下面的Spring的PetClinic示例应用中的例子一样)。 这种情况下,我们需要定义一个Hibernate的LocalSessionFactoryBean, 应用程序从中获取到Hibernate Session 实例。
The DataSource bean definition will be similar to one of the above examples, and is not shown. (If it is a container DataSource, it should be non-transactional as Spring, rather than the container, will manage transactions.) DataSource bean的定义同上例类似,这里不再展示。 (不过,如果是一个容器提供的DataSource, 它将由容器自身,而不是Spring,来管理事务)。
The ‘txManager‘ bean in this case is of class HibernateTransactionManager. In the same way as the DataSourceTransactionManager needs a reference to the DataSource, the HibernateTransactionManager needs a reference to the SessionFactory. 这种情况中‘txManager‘ bean的类型为HibernateTransactionManager。 同样地,DataSourceTransactionManager需要一个指向 DataSource的引用,而 HibernateTransactionManager需要一个指向 SessionFactory的引用。
org/springframework/samples/petclinic/hibernate/petclinic.hbm.xmlhibernate.dialect=${hibernate.dialect}With Hibernate and JTA transactions, we can simply use the JtaTransactionManager as with JDBC or any other resource strategy. 我们可以简单地使用JtaTransactionManager来处理Hibernate事务和JTA事务, 就像我们处理JDBC,或者任何其它的资源策略一样。
Note that this is identical to JTA configuration for any resource, as these are global transactions, which can enlist any transactional resource. 注意任何资源的JTA配置都是这样的,因为它们都是全局事务,可以支持任何事务性资源。
In all these cases, application code won‘t need to change at all. We can change how transactions are managed merely by changing configuration, even if that change means moving from local to global transactions or vice versa. 在所有这些情况下,应用程序代码根本不需要做任何改动。我们仅仅通过改变配置就可以改变事务如何被管理, 即使这些更改意味着在局部事务和全局事务间切换。
9.4. Resource synchronization with transactions 使用事务来同步资源
It should now be clear how different transaction managers are created, and how they are linked to related resources which need to be synchronized to transactions (i.e. DataSourceTransactionManager to a JDBC DataSource, HibernateTransactionManager to a Hibernate SessionFactory, etc). There remains the question however of how the application code, directly or indirectly using a persistence API (JDBC, Hibernate, JDO, etc), ensures that these resources are obtained and handled properly in terms of proper creation/reuse/cleanup and trigger (optionally) transaction synchronization via the relevant PlatformTransactionManager. 现在应该比较清楚了,不同的事务管理器是如何创建的,以及它们如何被连接到相应的 需要被同步到事务的资源上(例如,DataSourceTransactionManager 对应到JDBC DataSource, HibernateTransactionManager对应到Hibernate SessionFactory等)。可是,剩下的问题是, 直接或间接地使用一种持久化API(JDBC,Hibernate,JDO等)的应用代码,如何确保通过 相关的PlatformTransactionManager来恰当地 获取并操作资源,来满足事务同步(包括:创建/复用/清理 和 触发(可能没有))。
9.4.1. High-level approach 高层次方式
The preferred approach is to use Spring‘s highest level persistence integration APIs. These do not replace the native APIs, but internally handle resource creation/reuse, cleanup, optional transaction synchronization of the resources and exception mapping so that user data access code doesn‘t have to worry about these concerns at all, but can concentrate purely on non-boilerplate persistence logic. Generally, the same template approach is used for all persistence APIs, with examples such as JdbcTemplate, HibernateTemplate, JdoTemplate, etc These integration classes are detailed in subsequent chapters of this reference documentation. 首选的方法是使用Spring的高层持久化集成APIs。这种方式不会替换原始的APIs,而 是在内部封装了资源创建、复用、清理、事务同步以及异常映射等功能,这样 用户的数据访问代码就不必关心这些,而集中精力于自己的持久化逻辑。通常,对所有 持久化API都采用这种 模板 方法,例如 JdbcTemplate,HibernateTemplate, JdoTemplate等。这些集成功能类在这份参考文档后面的章节中详细叙述。
9.4.2. Low-level approach 低层次方式
At a lower level exist classes such as DataSourceUtils (for JDBC), SessionFactoryUtils (for Hibernate), PersistenceManagerFactoryUtils (for JDO), and so on. When it is preferable for application code to deal directly with the resource types of the native persistence APIs, these classes ensure that proper Spring-managed instances are obtained, transactions are (optionally) synchronized, and exceptions which happen in the process are properly mapped to a consistent API. 在较低层次上,有这些类: DataSourceUtils (for JDBC), SessionFactoryUtils (for Hibernate), PersistenceManagerFactoryUtils (for JDO),等等。 当对应用代码来说,直接同原始持久化API特有的资源类型打交道是更好的选择时,这些类 确保应用代码获取到正确的Spring受管bean,事务被正确同步,处理过程中的异常被映射 到一致的API。
For example, in the case of JDBC, instead of the traditional JDBC approach of calling the getConnection() method on the DataSource, you would instead use Spring‘s org.springframework.jdbc.datasource.DataSourceUtils class as follows: 例如,在JDBC环境下,你不再使用传统的调用DataSource 的getConnection()方法的方式,而是使用Spring的 org.springframework.jdbc.datasource.DataSourceUtils,像这样:
Connection conn = DataSourceUtils.getConnection(dataSource);If an existing transaction exists, and already has a connection synchronized (linked) to it, that instance will be returned. Otherwise, the method call will trigger the creation of a new connection, which will be (optionally) synchronized to any existing transaction, and made available for subsequent reuse in that same transaction. As mentioned, this has the added advantage that any SQLException will be wrapped in a Spring CannotGetJdbcConnectionException - one of Spring‘s hierarchy of unchecked DataAccessExceptions. This gives you more information than can easily be obtained from the SQLException, and ensures portability across databases: even across different persistence technologies. 如果已有一个事务及与之关联的connection存在,该实例将被返回。否则, 该方法调用将触发起一个新的connection的创建动作,该connection(可选地)被同步到任何现有的事务, 并可以在同一事务范围内被后续的调用复用。正如上面提到的, 这个过程有一个额外的好处,就是任何SQLException将被包装为Spring的 CannotGetJdbcConnectionException, 该类是Spring的unchecked的DataAccessExceptions层次体系中的一员。这将给你比从 SQLException中简单所得更多的信息,而且保证了跨 数据库——甚至其他持久化技术——的移植性。
It should be noted that this will also work fine without Spring transaction management (transaction synchronization is optional), so you can use it whether or not you are using Spring for transaction management. 应该指出的是,这些类同样可以在没有Spring事务管理的环境中良好工作(事务同步能力是可选的), 所以无论你是否使用Spring的事务管理,你都可以使用这些类。
Of course, once you‘ve used Spring‘s JDBC support or Hibernate support, you will generally prefer not to use DataSourceUtils or the other helper classes, because you‘ll be much happier working via the Spring abstraction than directly with the relevant APIs. For example, if you use the Spring JdbcTemplate or jdbc.object package to simplify your use of JDBC, correct connection retrieval happens behind the scenes and you won‘t need to write any special code. 当然,一旦你用过Spring的JDBC支持或Hibernate支持,你一般就不再会选择 DataSourceUtils或是别的辅助类了,因为你会更乐意 与Spring抽象一起工作,而不是直接使用相关的API。例如,如果你使用Spring JdbcTemplate或jdbc.object包来 简化使用JDBC,Spring会在幕后替你正确地获取连接,而你不需要写任何特殊代码。
9.4.3. TransactionAwareDataSourceProxy
At the very lowest level exists the TransactionAwareDataSourceProxy class. This is a proxy for a target DataSource, which wraps the target DataSource to add awareness of Spring-managed transactions. In this respect, it is similar to a transactional JNDI DataSource as provided by a J2EE server. TransactionAwareDataSourceProxy类工作在底层。 这是一个对目标DataSource的代理,它包装了 目标DataSource,提供对Spring管理事务的可知性。 在这点上,它类似于一个J2EE服务器提供的事务性JNDI DataSource。
It should almost never be necessary or desirable to use this class, except when existing code exists which must be called and passed a standard JDBC DataSource interface implementation. In that case, it‘s possible to still have this code be usable, but participating in Spring managed transactions. It is preferable to write your new code using the higher level abstractions mentioned above. 该类应该永远不需要被应用代码使用,除非现有代码存在需要直接传递一个标准的JDBC DataSource的情况。这时可以通过参与Spring 管理事务让这些代码仍然有用。书写新的代码时,首选的方法是采用上面提到的Spring 高层抽象。
9.5. Declarative transaction management 声明式事务管理
Most Spring users choose declarative transaction management. It is the option with the least impact on application code, and hence is most consistent with the ideals of a non-invasive lightweight container. 大多数Spring用户选择声明式事务管理。这是对应用代码影响最小的选择,因此也最符合 非侵入式轻量级容器的理念。Spring‘s declarative transaction management is made possible with Spring AOP, although, as the transactional aspects code comes with Spring and may be used in a boilerplate fashion, AOP concepts do not generally have to be understood to make effective use of this code. Spring的声明式事务管理是通过Spring AOP实现的,因为事务方面的代码与Spring绑定并 以一种样板式风格使用,不过尽管如此,你一般并不需要理解AOP概念就可以有效地使用 Spirng的声明式事务管理。
It may be helpful to begin by considering EJB CMT and explaining the similarities and differences with Spring declarative transaction management. The basic approach is similar: it is possible to specify transaction behavior (or lack of it) down to individual method level. It is possible to make a setRollbackOnly() call within a transaction context if necessary. The differences are: 从考虑EJB CMT和Spring声明式事务管理的相似以及不同之处出发是很有益的。 它们的基本方法是相似的:都可以指定事务管理到单独的方法;如果需要可以在事务上下文调用 setRollbackOnly()方法。不同之处如下:
Unlike EJB CMT, which is tied to JTA, Spring declarative transaction management works in any environment. It can work with JDBC, JDO, Hibernate or other transactions under the covers, with configuration changes only. 不象EJB CMT绑定在JTA上,Spring声明式事务管理可以在任何环境下使用。 只需更改配置文件,它就可以和JDBC、JDO、Hibernate或其他的事务机制一起工作。
Spring enables declarative transaction management to be applied to any class (and attendant instances of that class), not merely special classes such as EJBs. Spring的声明式事务管理可以被应用到任何类(以及那个类的实例)上,不仅仅是像 EJB那样的特殊类。
Spring offers declarative rollback rules: a feature with no EJB equivalent, which we‘ll discuss below. Rollback can be controlled declaratively, not merely programmatically. Spring提供了声明式的回滚规则:EJB没有对应的特性, 我们将在下面讨论。回滚可以声明式的控制,不仅仅是编程式的。
Spring gives you an opportunity to customize transactional behavior, using AOP. For example, if you want to insert custom behavior in the case of transaction rollback, you can. You can also add arbitrary advice, along with the transactional advice. With EJB CMT, you have no way to influence the container‘s transaction management other than setRollbackOnly(). Spring允许你通过AOP定制事务行为。例如,如果需要,你可以在事务回滚中插入定制的行为。 你也可以增加任意的通知,就象事务通知一样。使用EJB CMT,除了使用setRollbackOnly(), 你没有办法能够影响容器的事务管理。
Spring does not support propagation of transaction contexts across remote calls, as do high-end application servers. If you need this feature, we recommend that you use EJB. However, consider carefully before using such a feature. Normally, we do not want transactions to span remote calls. Spring不提供高端应用服务器提供的跨越远程调用的事务上下文传播。如果你需要这些特性, 我们推荐你使用EJB。然而,不要轻易使用这些特性。通常我们并不希望事务跨越远程调用。
Where is TransactionProxyFactoryBean? TransactionProxyFactoryBean在哪儿?
Declarative transaction configuration in versions of Spring 2.0 and above differs considerably from previous versions of Spring. The main difference is that there is no longer any need to configure TransactionProxyFactoryBean beans. Spring2.0及以后的版本中声明式事务的配置与之前的版本有相当大的不同。 主要差异在于不再需要配置TransactionProxyFactoryBean了。
The old, pre-Spring 2.0 configuration style is still 100% valid configuration; under the covers think of the new as simply defining TransactionProxyFactoryBean beans on your behalf. Spring2.0之前的旧版本风格的配置仍然是有效的;你可以简单地认为新的 替你定义了 TransactionProxyFactoryBean。
The concept of rollback rules is important: they enable us to specify which exceptions (and throwables) should cause automatic roll back. We specify this declaratively, in configuration, not in Java code. So, while we can still call setRollbackOnly() on the TransactionStatus object to roll the current transaction back programmatically, most often we can specify a rule that MyApplicationException should always result in roll back. This has the significant advantage that business objects don‘t need to depend on the transaction infrastructure. For example, they typically don‘t need to import any Spring APIs, transaction or other. 回滚规则的概念比较重要:它使我们能够指定什么样的异常(和throwables)将导致自动回滚。 我们在配置文件中声明式地指定,无须在Java代码中。同时,我们仍旧可以通过调用 TransactionStatus的setRollbackOnly() 方法编程式地回滚当前事务。通常,我们定义一条规则,声明 MyApplicationException应该总是导致事务回滚。这种方式 带来了显著的好处,它使你的业务对象不必依赖于事务设施。典型的例子是你不必在代码中导入Spring API,事务等。
While the EJB default behavior is for the EJB container to automatically roll back the transaction on a system exception (usually a runtime exception), EJB CMT does not roll back the transaction automatically on an application exception (i.e. a checked exception other than java.rmi.RemoteException). While the Spring default behavior for declarative transaction management follows EJB convention (roll back is automatic only on unchecked exceptions), it‘s often useful to customize this. 对EJB来说,默认的行为是EJB容器在遇到系统异常(通常指运行时异常)时自动回滚当前事务。 EJB CMT遇到应用异常(例如,除了java.rmi.RemoteException外别的checked exception)时 并不会自动回滚。默认式Spring处理声明式事务管理的规则遵守EJB习惯(只在遇到unchecked exceptions时自动回滚), 但通常定制这条规则会更有用。
9.5.1. Understanding Spring‘s declarative transaction implementation 理解Spring的声明式事务管理实现
The aim of this section is to dispel the mystique that is sometimes associated with the use of declarative transactions. It is all very well for this reference documentation simply to tell you to annotate your classes with the @Transactional annotation, add the line (‘‘) to your configuration, and then expect you to understand how it all works. This section will explain the inner workings of Spring‘s declarative transaction infrastructure to help you navigate your way back upstream to calmer waters in the event of transaction-related issues. 本节的目的是消除与使用声明式事务管理有关的神秘性。简单点儿总是好的,这份参考文档 只是告诉你给你的类加上@Transactional注解, 在配置文件中添加(‘‘)行, 然后期望你理解整个过程是怎么工作的。此节讲述Spring的声明式事务管理内部的工作机制, 以帮助你在面对事务相关的问题时不至于误入迷途,回朔到上游平静的水域。
Tip
Looking at the Spring source code is a good way to get a real understanding of Spring‘s transaction support. You should find the Javadoc informative and complete. We suggest turning the logging level to ‘DEBUG‘ in your Spring-enabled application(s) during development to better see what goes on under the hood. 阅读Spring源码是理解清楚Spring事务支持的一个好方法。Spring的Javadoc提供的信息丰富而完整。 我们建议你在开发自己的Spring应用时把日志级别设为‘DEBUG‘级,这样你能更清楚地看到幕后发生的事。
The most important concepts to grasp with regard to Spring‘s declarative transaction support are that this support is enabled via AOP proxies, and that the transactional advice is driven by metadata (currently XML- or annotation-based). The combination of a proxy with transactional metadata yields an AOP proxy that uses a TransactionInterceptor in conjunction with an appropriate PlatformTransactionManager implementation to drive transactions around method invocations. 在理解Spring的声明式事务管理方面最重要的概念是:Spring的事务管理是通过 AOP代理实现的。其中的事务通知由元数据 (目前基于XML或注解)驱动。代理对象与事务元数据结合产生了一个AOP代理,它使用 一个PlatformTransactionManager实现品配合 TransactionInterceptor,在方法调用 前后实施事务。
Note
Although knowledge of AOP (and specifically Spring AOP) is not required in order to use Springs declarative transaction support, it can help. Spring AOP is thoroughly covered in the chapter entitledChapter 6, Aspect Oriented Programming with Spring 使用Spring进行面向切面编程(AOP). 尽管使用Spring声明式事务管理不需要AOP(尤其是Spring AOP)的知识,但了解这些是很有帮助的。 你可以在Chapter 6, Aspect Oriented Programming with Spring 使用Spring进行面向切面编程(AOP)章找到关于Spring AOP的全部内容。
Conceptually, calling a method on a transactional proxy looks like this... 概念上来说,在事务代理上调用方法的工作过程看起来像这样:

9.5.2. A first example 第一个例子
Consider the following interface, and its attendant implementation. The intent is to convey the concepts, and using Foo and Bar means that you can concentrate on the transaction usage and not have to worry about the domain model. 请看下面的接口和它的实现。这个例子的意图是介绍概念,使用 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();}}(For the purposes of this example, the fact that the implementation class (DefaultFooService) throws UnsupportedOperationException instances in the body of each implemented method is good; it allows us to see transactions being created and then rolled back in response to UnsupportedOperationException instances being thrown.) (对该例的目的来说,上例中实现类(DefaultFooService) 的每个方法在其方法体中抛出UnsupportedOperationException的做法 是恰当的,我们可以看到,事务被创建出来,响应 UnsupportedOperationException的抛出,然后回滚。)
Let‘s assume that the first two methods of the FooService interface (getFoo(String) and getFoo(String, String)) have to execute in the context of a transaction with read-only semantics, and that the other methods (insertFoo(Foo) and updateFoo(Foo)) have to execute in the context of a transaction with read-write semantics. 我们假定,FooService的前两个方法( getFoo(String)和getFoo(String, String))必须 执行在只读事务上下文中,其余方法(insertFoo(Foo)和 updateFoo(Foo))必须执行在读写事务上下文中。
To configure this scenario in a declarative fashion using XML-based metadata, you would write the following configuration (don‘t worry about taking it in all at once; everything will be explained in detail in the next few paragraphs). 使用XML方式元数据的声明式配置的话,你得这么写(不要想着一次全部理解,所有内容会在后面的章节详细讨论):
Let‘s pick apart the above configuration. We have a service object (the ‘fooService‘ bean) that we want to make transactional. The transaction semantics that we want to apply are encapsulated in the definition. The definition reads as “... all methods on starting with ‘get‘ are to execute in the context of a read-only transaction, and all other methods are to execute with the default transaction semantics”. The ‘transaction-manager‘ attribute of the tag is set to the name of the PlatformTransactionManager bean that is going to actually drive the transactions (in this case the ‘txManager‘ bean). 我们来分析一下上面的配置。我们要把一个服务对象(‘fooService‘ bean) 做成事务性的。我们想施加的事务语义封装在 定义中。“ 把所有以‘get‘开头的方法看做执行在只读事务上下文中,其余的 方法执行在默认语义的事务上下文中”。其中的 ‘transaction-manager‘属性被设置为一个指向 PlatformTransactionManager bean的名字 (这里指‘txManager‘),该bean将实际上实施事务管理。
Tip
You can actually omit the ‘transaction-manager‘ attribute in the transactional advice () if the bean name of the PlatformTransactionManager that you want to wire in has the name ‘transactionManager‘. If the PlatformTransactionManager bean that you want to wire in has any other name, then you have to be explicit and use the ‘transaction-manager‘ attribute as in the example above. 事实上,如果PlatformTransactionManager bean 的名字是‘transactionManager‘的话,你的事务通知() 中的‘transaction-manager‘属性可以忽略。否则你则需要像上例那样明确指定。
The final piece of the configuration is the definition. This ensures that the transactional advice defined by the ‘txAdvice‘ bean actually gets to execute at the appropriate points in the application. First we define a pointcut that matches the execution of any operation defined in the FooService interface (and we called this pointcut ‘fooServiceOperation‘). Then we combine the pointcut with the ‘txAdvice‘ using an advisor. The result indicates that at the execution of a ‘fooServiceOperation‘, the advice defined by ‘txAdvice‘ will be run. 配置中最后一段是的定义,它确保由 ‘txAdvice‘ bean定义的事务通知在应用中合适的点被执行。首先我们定义了 一个切面,它匹配FooService接口定义的所有操作, 我们把该切面叫做‘fooServiceOperation‘。然后我们用一个通知器 (advisor)把这个切面与‘txAdvice‘绑定在一起,表示当 ‘fooServiceOperation‘执行时,‘txAdvice‘定义的 通知逻辑将被执行。
The expression defined within the element is an AspectJ pointcut expression; see the chapter entitledChapter 6, Aspect Oriented Programming with Spring 使用Spring进行面向切面编程(AOP) for more details on pointcut expressions in Spring 2.0. 元素定义是AspectJ的切面表示法,可参考Spring 2.0Chapter 6, Aspect Oriented Programming with Spring 使用Spring进行面向切面编程(AOP)章获得更详细的内容。
A common requirement is to make an entire service layer transactional. The best way to do this is simply to change the pointcut expression to match any operation in your service layer. For example: 一个普遍性的需求是让整个服务层成为事务性的。满足该需求的最好方式是让切面 表达式匹配服务层的所有操作方法。例如:
(This example assumes that all your service interfaces are defined in the ‘x.y.service‘ package, again, see the chapter entitledChapter 6, Aspect Oriented Programming with Spring 使用Spring进行面向切面编程(AOP) for more details.) (这个例子中假定你所有的服务接口定义在‘x.y.service‘ 包中。你同样可以参考Chapter 6, Aspect Oriented Programming with Spring 使用Spring进行面向切面编程(AOP)章获得更详细内容。)
Now that we‘ve analyzed the configuration, you may be asking yourself, “Okay... but what does all this configuration actually do?”. 现在,既然我们已经分析了整个配置,你可能会问了,“好吧, 但是所有这些配置做了什么?”。
What the above configuration is going to do is create a transactional proxy around the object that is created from the ‘fooService‘ bean definition. The proxy will be configured with the transactional advice, so that when an appropriate method is invoked on the proxy, a transaction may be started, suspended, be marked as read-only, etc, depending on the transaction semantics associated with that method. 上面的配置将为由‘fooService‘定义的bean创建一个代理对象, 这个代理对象被装配了事务通知,所以当它的相应方法被调用时,一个事务将被启动、 挂起、被标记为只读,或者其它(根据该方法所配置的事务语义)。
Let‘s consider the following driver program to test the above configuration. 我们来看看下面的例子,测试一下上面的配置。
public final class Boot {public static void main(final String[] args) throws Exception {ApplicationContext ctx = new ClassPathXmlApplicationContext("context.xml", Boot.class);FooService fooService = (FooService) ctx.getBean("fooService");fooService.insertFoo (new Foo());}}The output from running the above program will look something like this. (Please note that the Log4J output and the attendant stacktrace from the UnsupportedOperationException thrown by the insertFoo(..) method of the DefaultFooService class have been truncated in the interest of clarity.) 运行上面程序的输出结果看起来像这样(注意为了看着清楚,Log4j的消息和异常堆栈信息被省略了)。
[AspectJInvocationContextExposingAdvisorAutoProxyCreator] - Creating implicit proxyfor bean ‘fooService‘ with 0 common interceptors and 1 specific interceptors[JdkDynamicAopProxy] - Creating JDK dynamic proxy for [x.y.service.DefaultFooService][TransactionInterceptor] - Getting transaction for x.y.service.FooService.insertFoo[DataSourceTransactionManager] - Creating new transaction with name [x.y.service.FooService.insertFoo][DataSourceTransactionManager] - Acquired Connection[org.apache.commons.dbcp.PoolableConnection@a53de4] for JDBC transaction[RuleBasedTransactionAttribute] - Applying rules to determine whether transaction shouldrollback on java.lang.UnsupportedOperationException[TransactionInterceptor] - Invoking rollback for transaction on x.y.service.FooService.insertFoodue to throwable [java.lang.UnsupportedOperationException][DataSourceTransactionManager] - Rolling back JDBC transaction on Connection[org.apache.commons.dbcp.PoolableConnection@a53de4][DataSourceTransactionManager] - Releasing JDBC Connection after transaction[DataSourceUtils] - Returning JDBC Connection to DataSourceException in thread "main" java.lang.UnsupportedOperationExceptionat x.y.service.DefaultFooService.insertFoo(DefaultFooService.java:14)at $Proxy0.insertFoo(Unknown Source)at Boot.main(Boot.java:11)
9.5.3. Applying different transactional semantics to different beans 为不同的bean应用不同的事务语义
Let‘s now consider the scenario where you have a number of service layer objects, and you want to apply totally different transactional semantics to each group of those objects. You can effect this in Spring by defining any number of distinct elements with differing ‘pointcut‘ and ‘advice-ref‘ attribute values. 现在我们考虑一下这样的场景,你有许多服务对象,而且想为不同组的对象设置 完全不同 的事务语义。 在Spring中,你可以通过定义各自特定的 元素, 每个advisor采用不同的 ‘pointcut‘ 和 ‘advice-ref‘ 属性,来达到目的。
By way of an example, let‘s assume that all of your service layer classes are defined in a root ‘x.y.service‘ package. To make all beans that are instances of classes (or - better - implementations of interfaces) defined in the service package (or in subpackages) and that have names ending in ‘Service‘ have the default transactional semantics, you could write the following configuration: 借助于一个例子,我们假定你所有的服务层类定义在以 ‘x.y.service‘ 为根的包内,为了让service包(或子包)下所有名字以 ‘Service‘ 结尾的类的对象(或者,更好的做法是,接口的实现类的对象)拥有默认的事务语义,你可以配置如下:

9.5.4. Using @Transactional 使用@Transactional
Note
Please be aware that the functionality offered by the @Transactional annotation and the attendant support classes is only available to you if you are using at least Java 5 (Tiger). 注意:@Transactional 注解及其支持类仅适用于Java5(Tiger)。
In addition to the XML-based declarative approach to transaction configuration, you can also use an annotation-based declarative approach to transaction configuration via the @Transactional annotation. 除了基于XML文件的声明式事务配置外,你也可以采用注解式的事务配置方法——通过 @Transactional 注解。
Declaring transaction semantics directly in the Java source code puts the declarations much closer to the affected code, and there is generally not much danger of undue coupling, since typically, code that is deployed as transactional is always deployed that way. 直接在Java源代码中声明事务语义的做法让事务声明和将受其影响的代码距离更近了, 而且一般来说不会有不恰当的耦合的风险,因为,典型情况下, 被部署为事务性的代码几乎总是运行在事务环境中。
The ease-of-use afforded by the use of the @Transactional annotation is best illustrated with an example, after which all of the details will be explained. Consider the following interface definition: 下面的例子很好地演示了 @Transactional 的易用性, 随后解释其中的细节。先看看其中的接口定义:
@Transactionalpublic interface FooService {Foo getFoo(String fooName);Foo getFoo(String fooName, String barName);void insertFoo(Foo foo);void updateFoo(Foo foo);}Implementations of the above FooService interfaces that are declared as beans in the Spring container can be made transactional by adding merely one line of XML configuration, like so: 在Spring配置文件中,上面 FooService 的实现类的bean可以仅仅通过 一 行xml配置为事务性的。如下:
The @Transactional annotation may be placed before an interface definition, a method on an interface, a class definition, or a method on a class. @Transactional 可以被应用于接口定义和接口方法、类定义和类方法上。
Note
The mere presence of the @Transactional annotation is not enough to actually turn on the transactional behavior - the @Transactional annotation is simply metadata that can be consumed by something that is @Transactional-aware and that can use the transactional metadata to apply transactional behavior. 仅仅 @Transactional 的出现不足于开启事务行为, 它仅仅是一种元数据,能够被可识别该注解并应用事务行为的代码所使用。
In the case of the above example, it is the presence of the element that switches on the transactional behavior. 上面的例子中,其实正是 元素的出现 开启 了事务行为。
In keeping with one of the Spring framework‘s central tenets, namely ‘do the intuitive thing‘, Spring‘s treatment of the @Transactional annotation with regard to inheritance just makes sense. If you annotate an interface at the class level with the @Transactional annotation, then all implementations of that interface will inherit the transaction settings applied to the interface. This is the direct opposite of the usual semantics that apply to annotations, where annotations on interfaces and methods are never inherited. With Spring, you can override the default transaction settings that may be inherited from a superclass or interface by specifying your own @Transactional values; basically, the most derived location takes precedence when evaluating the transaction semantics of a method. In the case of the following example, the FooServiceinterface is annotated with default transaction settings, but the @Transactional annotation on the updateFoo(Foo) method on the DefaultFooService class takes precedence over the transactional settings inherited from the FooService interface. 为了符合Spring的核心原则之一,即“按直觉做事”,Spring中对 @Transactional 的处理方式考虑了继承性,这是有意义的。 如果你在类层次上给一个接口加了 @Transactional 注解, 那么所有实现该接口的类将继承施加在接口上的事务设置。这与注解本来的含义截然不同, 通常加在接口和方法上的注解 从不 会被继承。 使用Spring,你可以通过指定自己的 @Transactional 来覆盖从接口或超类自动继承的事务设置。基本上,确定一个方法的事务语义时最优先考虑继承树上最末端的类。 在下面的例子中,FooService 接口在类层次被注解为默认事务设置, 但其实现类 DefaultFooService 的方法 updateFoo(Foo) 上的 @Transactional 却有更高的优先级,覆盖了从接口继承来的默认设置。
@Transactional(readOnly = true)public interface FooService {Foo getFoo(String fooName);void updateFoo(Foo foo);}public class DefaultFooService implements FooService {public Foo getFoo(String fooName) {// do something//随便做点儿什么}// these settings have precedence// 这些 设置有较高的优先级@Transactional(readOnly = false, propagation = Propagation.REQUIRES_NEW)public void updateFoo(Foo foo) {// do something//随便做点儿什么}}Note
Please note that this ‘plain common sense‘ approach to inheriting @Transactional settings only applies in the case of SpringAOP-driven transaction management. If you are using another transaction management strategy, such as AspectJ-driven transactions, then the usual Java rules with regard to annotations apply (i.e. there is no inheritance). 注意,这种“纯粹直觉式”继承 @Transactional只 适用于Spring AOP驱动的事务管理环境。 如果你采用别的事务管理策略,例如AspectJ驱动事务,则关于Java注解的一般规则仍然起效 (即:没有继承)。
9.5.4.1. @Transactional settings @Transactional 有关的设置
Used in its simplest form, the @Transactional annotation specifies that an interface, class, or method must be transactional. The default transaction semantics are read/write, PROPAGATION_REQUIRED, ISOLATION_DEFAULT, TIMEOUT_DEFAULT, with rollback on a RuntimeException, but not Exception. 在最简单的形式下,@Transactional 指定一个接口、类、方法必须是事务性的,其默认事务语义为:read/write, PROPAGATION_REQUIRED, ISOLATION_DEFAULT,TIMEOUT_DEFAULT, 而且仅当遇到RuntimeException时回滚,而不是 Exception。
Optional properties of the annotation modify transaction settings. 改变事务设置的其他可选属性
Table 9.1. Properties of the Transactional Annotation Transactional 注解的属性
Property 属性 Type 类型 Description 描述
propagation 传播性 enum: Propagation 枚举型:Propagation optional propagation setting (defaults to PROPAGATION_REQUIRED) 可选的传播性设置 (默认值:PROPAGATION_REQUIRED)
isolation 隔离性 enum: Isolation 枚举型:Isolation optional isolation level (defaults to ISOLATION_DEFAULT) 可选的隔离性级别(默认值:ISOLATION_DEFAULT)
readOnly 只读性 boolean 布尔型 read/write vs. read-only transaction; defaults to false, i.e. transactions are read/write) 读写型事务 vs. 只读型事务(默认值:false,即只读型事务)
rollbackFor 回滚异常类(rollbackFor) an array of Class objects, which must be derived from Throwable 一组 Class 类的实例,必须是 Throwable 的子类 an optional array of exception classes which must cause rollback. By default, checked exceptions do not roll back, unchecked (RuntimeException derived) do cause roll back 一组异常类,遇到时 确保 进行回滚。 默认情况下checked exceptions不进行回滚, 仅unchecked exceptions(即RuntimeException的子类)才进行事务回滚
rollbackForClassname 回滚异常类名(rollbackForClassname) an array of class names. Classes must be derived from Throwable 一组 Class 类的名字,必须是 Throwable的子类 an optional array of names of exception classes that must cause rollback 一组异常类名,遇到时 确保 进行回滚
noRollbackFor 不回滚异常类(noRollbackFor) an array of Class objects, which must be derived from Throwable 一组 Class 类的实例,必须是 Throwable 的子类 an optional array of exception classes that must not cause rollback. 一组异常类,遇到时确保 不 回滚。
noRollbackForClassname 不回滚异常类名(noRollbackForClassname) an array of String class names, which must be derived from Throwable 一组 Class 类的名字,必须是 Throwable 的子类 an optional array of names of exception classes which must not cause rollback 一组异常类,遇到时确保 不 回滚
We recommend you consult the Javadoc for the @Transactional annotation since it describes the above properties and their attendant values in much greater detail. 我们推荐你参考 @Transactional 注解的javadoc, 其中详细列举了上述各项属性及其可选值。
9.5.5. Advising transactional operations 插入事务操作
Consider the situation where you have an instance of a class, and you would like to execute both transactional and (to keep things simple) some basic profiling advice . So how do you effect this in the context of using ? 考虑这样的情况,你有一个类的实例,而且希望 同时 插入事务性通知(advice)和一些简单的测量(profiling)通知。那么,在 环境中该怎么做?
What we want to see when we invoke the updateFoo(Foo) method is a) some profiling aspect starting up, then b) transactional advice being applied (by starting or participating in a transaction depending on the configured transactional metadata), then c) the method on the advised object executing, then d) the transaction committing (we‘ll assume a sunny day scenario here), and then finally e) the profiling aspect reporting (somehow) exactly how long the whole method invocation took. 我们调用 updateFoo(Foo) 方法时希望这样: a)、测量(profiling)方面(aspect)的代码启动,然后 b)、进入事务通知(根据配置创建一个新事务或加入一个已经存在的事务),然后 c)、原始对象的方法执行,然后 d)、事务提交(我们假定这里一切正常),最后 e)、测量方面(aspect)报告整个执行过程花了多少时间。
Note
This chapter is not concerned with explaining AOP (except as it applies to transactions). Please see the chapter entitledChapter 6, Aspect Oriented Programming with Spring 使用Spring进行面向切面编程(AOP) for detailed coverage of the various bits and pieces of the following AOP configuration (and AOP in general). 这章不是专门讲述AOP的(除了应用于事务方面的之外)。请参考Chapter 6, Aspect Oriented Programming with Spring 使用Spring进行面向切面编程(AOP) 章以获得对各种AOP配置及其一般概念的详细叙述。
Here is the code for a (naively) simple profiling aspect. (Please note that the ordering of advice is controlled via the Ordered interface. For full details on advice ordering, consult the section entitledSection 6.2.4.7, “Advice ordering 通知(Advice)顺序”.) 这里有一份简单的(还不怎么成熟)测量方面(profiling aspect)的代码。 (注意,通知的顺序由 Ordered 接口控制。要想了解更多细节,请参考Section 6.2.4.7, “Advice ordering 通知(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;public int getOrder() {return this.order;}public void setOrder(int order) {this.order = order;}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;}}Here is the attendant configuration that will effect what we want. 这里是帮助满足我们上述要求的配置数据。
!-- 在测量通知后执行(order属性) -->其他的,例如:DataSource,以及PlatformTransactionManagerThe result of the above configuration will be a ‘fooService‘ bean that has profiling and transactional aspects applied to it in that order. If we wanted the profiling advice to execute after the transactional advice on the way in, and before the transactional advice on the way out, then we would simply swap the value of the profiling aspect bean‘s ‘order‘ property such that it was higher than the transactional advice‘s order value. 上面配置的结果是创建了一个 ‘fooService‘ bean, 测量方面和事务方面被 依照顺序 施加其上。 如果我们希望测量通知在目标方法执行之前 后于 事务通知执行, 而且在目标方法执行之后 先于 事务通知,我们可以简单地交换两个通知bean的order值。
The configuration of any number of additional aspects is effected in a similar fashion. 如果配置中包含更多的方面,它们将以同样的方式受到影响。
9.5.6. Using @Transactional with AspectJ 结合AspectJ使用@Transactional
It is also possible to use Spring‘s @Transactional support outside of a Spring container by means of an AspectJ aspect shipped in spring-aspects.jar. To use this support you first annotate types and methods with the @Transactional annotation, and then you must link (weave) your application with the org.springframework.transaction.aspectj.AnnotationTransactionAspect defined in spring-aspects.jar. The aspect must also be configured with a transaction manager. You could of course use Spring to dependency inject the aspect, but since we‘re focusing here on applications running outside of a Spring container, we‘ll show you how to do it programmatically 通过 spring-aspects.jar 提供的AspectJ方面, 你也可以在Spring容器之外使用Spring的 @Transactional 功能。 要使用这项功能首先你得给相应的类型和方法加上 @Transactional 注解,然后把 spring-aspects.jar 中定义的 org.springframework.transaction.aspectj.AnnotationTransactionAspect 方面连接进(织入)你的应用。同样,该方面必须配置一个事务管理器; 你当然可以通过Spring注入,但因为我们这里关注于在Spring容器之外运行应用, 我们将向你展示如何通过手动书写代码来完成。
Note
Prior to continuing, you may well want to read the previous sections entitledSection 9.5.4, “Using @Transactional 使用@Transactional ” andChapter 6, Aspect Oriented Programming with Spring 使用Spring进行面向切面编程(AOP) respectively. 在我们继续之前,你可能需要好好读一下前面的Section 9.5.4, “Using @Transactional 使用@Transactional ” 和Chapter 6, Aspect Oriented Programming with Spring 使用Spring进行面向切面编程(AOP) 两章。
// construct an appropriate transaction manager//创建一个适当的事务管理器DataSourceTransactionManager txManager = new DataSourceTransactionManager(getDataSource());// configure the AnnotationTransactionAspectto use it, this must be done before executing any transactional methods//执行任何事务方法之前,先配置好 AnnotationTransactionAspectAnnotationTransactionAspect.aspectOf().setTransactionManager (txManager); When using this aspect the @Transactional annotation can be used to annotate a type or interface as transactional, and subsequently the execution of any public operation defined by that type will have transactional semantics. Transactional semantics for an individual public method can be defined by annotating the corresponding method definition. Note that if annotating interface members (as opposed to methods implementing the interface), the interface itself should also be annotated as @Transactional. 使用此方面(aspect),@Transactional 可以把一个类或接口注解为事务性的, 随后该类型定义的任何 公有 操作将拥有事务语义。 单独一个 公有 方法的事务语义可以通过单独注解相应的方法定义。 注意,如果注解了接口成员(不同于接口方法的实现方法),接口本身也应该被加上@Transactional注解。
To weave your applications with the AnnotationTransactionAspect you must either build your application with AspectJ (see theAspectJ Development Guide) or use load-time weaving. SeeSection 6.7.4, “Using AspectJ Load-time weaving (LTW) with Spring applications 在Spring应用中使用AspectJ Load-time weaving(LTW)” for a discussion of load-time weaving with AspectJ. 要把AspectJ织入你的应用,你或者基于AspectJ构建你的应用(参考AspectJ Development Guide),或者采取“载入时织入”(load-time weaving),参考Section 6.7.4, “Using AspectJ Load-time weaving (LTW) with Spring applications 在Spring应用中使用AspectJ Load-time weaving(LTW)” 获得关于使用AspectJ进行“载入时织入”的讨论。
9.6. The default transaction settings 默认事务设置
The default transaction settings (semantics) are as follows: 默认情况下事务设置(语义)如下:
Exception handling: A RuntimeException causes roll-back, whereas a normal (checked) Exception does not. 异常处理:RuntimeException 导致回滚,而普通异常(checked )则不会
Transactions are read/write. 事务可读可写
Isolation level: TransactionDefinition.ISOLATION_DEFAULT 隔离级别:TransactionDefinition.ISOLATION_DEFAULT
Timeout: TransactionDefinition.TIMEOUT_DEFAULT 超时设置:TransactionDefinition.TIMEOUT_DEFAULT
The org.springframework.transaction.TransactionDefinition interface contains comprehensive Javadoc-style information concerning the above settings, and so will not be repeated here. org.springframework.transaction.TransactionDefinition 接口的javadoc提供了关于上述设置的丰富的信息,这里就不再重复了。
9.7. Programmatic transaction management 编程式事务管理
Spring provides two means of programmatic transaction management: Spring提供两种方式的编程式事务管理:
Using the TransactionTemplate 使用 TransactionTemplate
Using a PlatformTransactionManager implementation directly 直接使用一个 PlatformTransactionManager 实现
If you are going to use programmatic transaction management, the Spring team generally recommend the first approach (i.e. using the TransactionTemplate). The second approach is similar to using the JTA UserTransaction API (although exception handling is less cumbersome). 如果你选择编程式事务管理,Spring小组推荐你采用第一种方法(即使用 TransactionTemplate)。第二种方法类似使用JTA的 UserTransaction API (除了异常处理简单点儿)。
9.7.1. Using the TransactionTemplate 使用 TransactionTemplate
The TransactionTemplate adopts the same approach as other Spring templates such as JdbcTemplate and HibernateTemplate. It uses a callback approach, to free application code from having to do the boilerplate acquisition and release of resources (i.e. no more try/catch/finally/try/catch blocks.) Like many of the other template classes in Spring, a TransactionTemplate instance is threadsafe. TransactionTemplate采用与Spring中别的 模板 同样的方法,如 JdbcTemplate 和 HibernateTemplate。它使用回调机制, 将应用代码从样板式的资源获取和释放代码中解放出来,不再有大量的try/catch/finally/try/catch代码块。 同样,和别的模板类一样,TransactionTemplate 类的实例是线程安全的。
Application code that must execute in a transaction context looks like this. Note that the TransactionCallback can be used to return a value: 必须在事务上下文中执行的应用代码看起来像这样: (注意使用 TransactionCallback 可以有返回值)
Object result = tt.execute(new TransactionCallback() {public Object doInTransaction(TransactionStatus status) {updateOperation1();return resultOfUpdateOperation2();}});If there is no return value, use the convenient TransactionCallbackWithoutResult class via a Java anonymous class like so: 如果不需要返回值,更方便的方式是创建一个 TransactionCallbackWithoutResult的匿名类:
tt.execute(new TransactionCallbackWithoutResult() {protected void doInTransactionWithoutResult(TransactionStatus status) {updateOperation1();updateOperation2();}});Code within the callback can roll the transaction back by calling the setRollbackOnly() method on the supplied TransactionStatus object. 回调方法内的代码可以通过调用 TransactionStatus 对象的 setRollbackOnly() 方法来回滚事务。
Application classes wishing to use the TransactionTemplate must have access to a PlatformTransactionManager (which will typically be supplied to the class via dependency injection). It is easy to unit test such classes with a mock or stub PlatformTransactionManager. There is no JNDI lookup or static shenanigans here: it is a simple interface. As usual, you can use Spring to greatly simplify your unit testing. 想要使用 TransactionTemplate 的应用类必须能访问一个 PlatformTransactionManager(典型情况下通过依赖注入提供)。 这样的类很容易做单元测试,只需要引入一个 PlatformTransactionManager 的伪类或桩类。 这里没有JNDI查找、没有 静态 诡计,它是一个如此简单的接口。 像往常一样,使用Spring给你的单元测试带来极大地简化。
9.7.2. Using the PlatformTransactionManager 使用 PlatformTransactionManager
You can also use the org.springframework.transaction.PlatformTransactionManager directly to manage your transaction. Simply pass the implementation of the PlatformTransactionManager you‘re using to your bean via a bean reference. Then, using the TransactionDefinition and TransactionStatus objects you can initiate transactions, rollback and commit. 你也可以直接使用 org.springframework.transaction.PlatformTransactionManager 的实现来管理事务。只需通过bean引用简单地传入一个 PlatformTransactionManager 实现,然后 使用 TransactionDefinition 和 TransactionStatus 对象, 你就可以启动一个事务,提交或回滚。
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.8. Choosing between programmatic and declarative transaction management 选择编程式事务管理还是声明式事务管理
Programmatic transaction management is usually a good idea only if you have a small number of transactional operations. For example, if you have a web application that require transactions only for certain update operations, you may not want to set up transactional proxies using Spring or any other technology. In this case, using the TransactionTemplate may be a good approach. 当你只有很少的事务操作时,编程式事务管理通常比较合适。 例如,如果你有一个web应用,其中只有特定的更新操作有事务要求,你可能不愿使用Spring或其他技术设置事务代理。 这种情况下,使用 TransactionTemplate 可能 是个好办法。
On the other hand, if your application has numerous transactional operations, declarative transaction management is usually worthwhile. It keeps transaction management out of business logic, and is not difficult to configure in Spring. Using Spring, rather than EJB CMT, the configuration cost of declarative transaction management is greatly reduced. 另一方面,如果你的应用中存在大量事务操作,那么声明式事务管理通常是值得的。 它将事务管理与业务逻辑分离,而且在Spring中配置也不难。 使用Spring,而不是EJB CMT,声明式事务管理在配置上的成本极大地降低了。
9.9. Application server-specific integration 与特定应用服务器集成
Spring‘s transaction abstraction is generally application server agnostic. Additionally, Spring‘s JtaTransactionManager class, which can optionally perform a JNDI lookup for the JTA UserTransaction and TransactionManager objects, can be set to autodetect the location for the latter object, which varies by application server. Having access to the TransactionManager instance does allow enhanced transaction semantics. Please see the JtaTransactionManager Javadocs for more details. 一般来说,Spring的事务抽象与应用服务器是无关的。另外,使用Spring的 JtaTransactionManager 类时, 一种可选的方式是通过JNDI查询获得JTA UserTransaction 和 TransactionManager 对象,其中后者可以被设置为自动探测, 这时针对不同的应用服务器有不同的方式。能够直接访问 TransactionManager , 确实在很大程度上增强了事务语义,可以参考 JtaTransactionManager 类的javadoc获得更多细节。
9.9.1. BEA WebLogic
In a WebLogic 7.0, 8.1 or higher environment, you will generally prefer to use WebLogicJtaTransactionManager instead of the stock JtaTransactionManager class. This special WebLogic-specific subclass of the normal JtaTransactionManager. It supports the full power of Spring‘s transaction definitions in a WebLogic managed transaction environment, beyond standard JTA semantics: features include transaction names, per-transaction isolation levels, and proper resuming of transactions in all cases. 在一个使用WebLogic 7.0、8.1或更高版本的环境中,你一般会优先选用特定于Weblogic的 WebLogicJtaTransactionManager 类来取代基础的 JtaTransactionManager 类。在Weblogic环境中, 该类提供了对Spring事务定义的完全支持,超过了标准的JTA语义。 它的特性包括:支持事务名,支持为每个事务定义隔离级别,以及在任何环境下正确地恢复事务的能力。
9.9.2. IBM WebSphere
In a WebSphere 5.1, 5.0 and 4 environment, you may wish to use Spring‘s WebSphereTransactionManagerFactoryBean class. This is a factory bean which retrieves the JTA TransactionManager in a WebSphere environment, which is done via WebSphere‘s static access methods. (These methods are different for each version of WebSphere.) 在WebSphere 5.1、5.0和4环境下,你可以使用Spring的 WebSphereTransactionManagerFactoryBean 类。 这是一个工厂类,通过WebSphere的 静态 访问方法获取到JTA TransactionManager 实例。(这些静态方法在每个版本的WebSphere中都不同。)
Once the JTA TransactionManager instance has been obtained via this factory bean, Spring‘s JtaTransactionManager may be configured with a reference to it, for enhanced transaction semantics over the use of only the JTA UserTransaction object. 一旦通过工厂bean获取到JTA TransactionManager 实例, 就可以使用该实例装配一个Spring的 JtaTransactionManager bean, 它封装了JTA UserTransaction,提供增强的事务语义。
Please see the Javadocs for full details. 请参考相关javadoc以获得完整信息。
9.10. Solutions to common problems 公共问题的解决方案
9.10.1. Use of the wrong transaction manager for a specific DataSource 对一个特定的 DataSource 使用错误的事务管理器
Developers should take care to use the correct PlatformTransactionManager implementation for their requirements. It is important to understand how the Spring transaction abstraction works with JTA global transactions. Used properly, there is no conflict here: Spring merely provides a straightforward and portable abstraction. 开发者需要按照需求仔细地选择正确的 PlatformTransactionManager 实现。 理解Spring的事务抽象如何与JTA全局事务一起工作是非常重要的。 使用得当,就不会有任何冲突:Spring仅仅提供一个直观的、可移植的抽象层。
If you are using global transactions, you must use the Spring org.springframework.transaction.jta.JtaTransactionManager class (or anapplication server-specific subclass of it) for all your transactional operations. Otherwise Spring will attempt to perform local transactions on resources such as container DataSource instances. Such local transactions don‘t make sense, and a good application server will treat them as errors. 如果你使用全局事务,你 必须 为你的所有事务操作使用Spring的 org.springframework.transaction.jta.JtaTransactionManager 类 (或特定于某种应用服务器的子类)。 否则Spring将试图在象容器数据源这样的资源上执行局部事务。 这样的局部事务没有任何意义,好的应用服务器会把这些情况视为错误。