Spring 事务管理笔记

本文为 Spring 框架的事务管理学习笔记,官网文档地址为:Transaction Management
,隔离级别及传播属性解释来自 org.springframework.transaction.TransactionDefinition 文件注释。

Understanding the Spring Framework’s declarative transaction implementation 一节中,有大量实例代码帮助理解声明式事务管理的实现,本文中没有涉及。

为避免翻译后失意,及更准确的表达,部分内容我将直接用原文表述。

本文中会用 Spring 代表 Spring Framework。

1. Introduction to Spring Framework transaction management

让你使用 Spring 的不可抗拒的原因之一是其全面的事务支持,Spring 为事务管理提供的一致的抽象带来了如下的好处:

  • Consistent programming model across different transaction APIs such as Java transaction API(JTA), JDBC, Hibernate, Java Persistence API(JPA), and Java Data Objects(JDO).
  • Support for declarative transaction management.
  • Simpler API for programmatic transaction management than complex transaction APIs such as JTA.
  • Excellent integration with Spring’s data access abstractions.

2. Advantages of the Spring Framework’s transaction support model

传统上,对于Java EE 开发者而言,事务管理有两种选择:global transaction、local transaction。

全局事务让你能够在多种事务资源上使用,通常是有关系的数据库和队列。应用服务器通过 JTA 管理全局事务,而 JTA 的 API 相当笨重。JTA 的 UserTransaction 通常来自 JNDI,这意味着为了使用 JTA 你还需要使用 JNDI。显而易见,使用全局事务将会限制应用代码的服用,因此 JTA 通常只用在应用服务器环境中。

本地事务需要指定资源(resource-specific),比如一个事物关联一个 JDBC 连接。本地事务用起来很容易,但是有显著的缺点:不能跨越多个事务资源。

Spring 解决了上述两种事务的缺点,它能够让应用开发者再任何环境中使用一个一致的编程模型。You write your code once, and it can benefit from different different transaction management strategies in different environments. Spring 提供了声明式的(declarative)和编程的(programmatic)事务模型。许多用户更喜欢声明式的事务管理,在大多数情形下,这种做法是推荐的。

With programmatic transaction management, developers work with the Spring Framework 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 do not depend on the Spring Framework transaction API, or any other transaction API.

3. Understanding the Spring Framework transaction abstraction

Spring 事务构想的关键是事务策略(transaction strategy)的概念。transaction strategy 通过 org.springframework.transaction.PlatformTransactionManager 接口来定义:

public interface PlatformTransactionManager {
    TransactionStatus getTransaction(TransactionDefinition var1) throws TransactionException;

    void commit(TransactionStatus var1) throws TransactionException;

    void rollback(TransactionStatus var1) throws TransactionException;
}

首先,尽管它可以通过编程的方式在应用代码中使用,但它是一个 SPI(service provider interface)。因为 PlatformTransactionManager 是一个接口,它可以根据需要很容易的被 mock 或 stub。它也不和指定的查找策略绑定,比如 JNDI。PlatformTransactionManager 实现像 Spring IoC 容器中的其他对象一样定义。This benefit alone makes Spring Framework transactions a worthwhile abstraction even when you work with JTA. Transactional code can be tested much more easily than if it used JTA directly.

其次,保持了 Spring 的哲学,可以被任何 PlatformTransactionManager 接口的方法抛出的 TransactionException 异常是 unchecked。事务基础设施失败几乎总是致命的,在一些罕见的案例中,应用代码可以从事务失败的恢复,应用开发者也可以捕获来处理该异常。

getTransaction(..) 方法返回了一个 TransactionStatus 对象,取决于 TransactionDefinition 参数。TransactionStatus 或许表示一个新的事务,或许表示在当前的调用栈中已经存在一个事务。 The implication in this latter case is that, as with Java EE transaction contexts, a TransactionStatus is associated with a thread of execution.

TransactionDefinition 接口指定了:

  • Isolation:该事务与其他事务的隔离级别。例如,当前事务是否可以看到其他事务未提交的写入。
  • Propagation:Typically, all code executed within a transaction scope will run in that transaction. However, you have the option of specifying the behavior in the event that a transactional method is executed when a transaction context already exists. For example, code can continue running in the existing transaction (the common case); or the existing transaction can be suspended and a new transaction created.
  • Timeout:How long this transaction runs before timing out and being rolled back automatically by the underlying transaction infrastructure.
  • Read-only status: 当你的代码只读而不修改数据的时候可以使用一个只读事务。在某些情况下,只读事务是非常有用的优化,比如你在使用 Hibernate 的时候。

这些设置反映了标准的事务概念。如果必要,请查阅讨论事务隔离级别和其他核心事务概念的资料。理解这些概念对于使用 Spring 和 其他事务管理解决方法都是必要的。

TransactionStatus 接口为事务代码提供了一个简单的方式来控制事务执行和查询事务状态。这些概念都是相似的。

public interface TransactionStatus extends SavepointManager, Flushable {
    boolean isNewTransaction();

    boolean hasSavepoint();

    void setRollbackOnly();

    boolean isRollbackOnly();

    void flush();

    boolean isCompleted();
}

不管你选择声明式还是编程式的事务管理,定义正确的 PlatformTransactionManager 的实现都是必须的。我们通常通过依赖注入来定义这个实现。

PlatformTransactionManager 实现一般需要需要知道其工作的环境:JDBC,JTA,Hibernate 等。下面的代码来定义一个 PlatformTransactionManager 实现(work with JDBC)。

先定义一个 JDBC DataSource

<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
    <property name="driverClassName" value="${jdbc.driverClassName}" />
    <property name="url" value="${jdbc.url}" />
    <property name="username" value="${jdbc.username}" />
    <property name="password" value="${jdbc.password}" />
</bean>

与其关联的 PlatformTransactionManager 的 bean 定义将有一个引用指向 DataSource 的定义,如下:

<bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    <property name="dataSource" ref="dataSource"/>
</bean>

在官方文档中,还提供了使用 JTA、Hibernate 情况下,该如何定义 PlatformTransactionManager,有需要的朋友,可到官网查看。

在上边的例子中,应用代码不需要改变。你可以仅通过改变配置来改变事务的管理。

4. Synchronizing resources with transactions

现在你应该知道如何创建不同的事务管理器,以及这些事务管理器如何与资源关联。这部份描述

  1. 如何用代码直接或间接的使用持久化 API,比如 JDBC、Hibernate、JDO,确保这些资源正确地被创建、复用、清理。
  2. 事务同步怎么通过有关的 PlatformTransactionManager 被触发。

4.1 High-level synchronization approach

The preferred approach is to use Spring’s highest level template based persistence integration APIs or to use native ORM APIs with transaction- aware factory beans or proxies for managing the native resource factories. These transaction-aware solutions internally handle resource creation and reuse, cleanup, optional transaction synchronization of the resources, and exception mapping. Thus user data access code does not have to address these tasks, but can be focused purely on non-boilerplate persistence logic. Generally, you use the native ORM API or take a template approach for JDBC access by using the JdbcTemplate. These solutions are detailed in subsequent chapters of this reference documentation.

4.2 Low-lovel synchronization approach

Classes such as DataSourceUtils (for JDBC), EntityManagerFactoryUtils (for JPA), SessionFactoryUtils (for Hibernate), PersistenceManagerFactoryUtils (for JDO), and so on exist at a lower level. When you want the application code to deal directly with the resource types of the native persistence APIs, you use these classes to ensure that proper Spring Framework-managed instances are obtained, transactions are (optionally) synchronized, and exceptions that occur in the process are properly mapped to a consistent API.

4.3 TransactionAwareDataSourceProxy

5. Declarative transaction management

AOP 让声明式的事务管理称为可能。

  • Spring 的声明式事务管理可以用在任何环境中。
  • 可以再任何类上应用声明式事务管理,不只是像 EJB 的特殊类。
  • Spring 提供声明式的回滚规则(编程式的也有)。
  • Spring 让你能够通过使用 AOP 来自定义事务行为。
  • The Spring Framework does not support propagation of transaction contexts across remote calls, as do high-end application servers.

回滚规则的概念非常重要:回滚规则让你指定什么异常应该自动回滚。

EJB 容器对运行时异常默认自动回滚,对受检查异常不会自动回滚。Spring 声明式事务管理的默认行为遵守 EJB 的约定。

5.1 Understanding the Spring Framework's declarative transaction implementation

仅仅告诉你使用 @Transactional 来注解类,在配置上 @EnableTransactionManagement,而期待你理解它如何工作是不够的。

The most important concepts to grasp with regard to the Spring Framework’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 AOP with transactional metadata yields an AOP proxy that uses a TransactionInterceptor in conjunction with an appropriate PlatformTransactionManager implementation to drive transactions around method invocations.

概念上来说,调用一个事务代理看起来是这样的:

5.5 tx:advice/ settings

<tx:advice/> 的默认设置是:

  • Propagation setting is REQUIRED.
  • Isolation level is DEFAULT.
  • Transaction is read/write.
  • Transaction timeout defaults to the default timeout of the underlying transaction system, or none if timeouts are not supported.
  • Any RuntimeException triggers rollback, and any checked Exception does not.

5.6 Using @Transactional

@Transaction settings

Property Type Description
value String Optional qualifier specifying the transaction manager to be used.
propagation enum: Propagation Optional propagation setting.
isolation enum: Isolation Optional isolation level.
readonly boolean read/write vs. read-only transaction
timeout int (in seconds granularity) Transaction timeout.

6. Isolation level、Propagation

Isolation level

  1. ISOLATION_DEFAULT:使用相关数据库的默认隔离级别。所有其他的级别对应 JDBC 的隔离级别。
  2. ISOLATION_READ_UNCOMMITTED:可能会发生脏读、不可重复读、幻读。
  3. ISOLATION_READ_COMMITTED:不会发生脏读,但有可能发生不可重复读和幻读。
  4. ISOLATION_REPEATABLE_READ :不会发生脏读和不可重复读,但是有可能发生幻读。
  5. ISOLATION_SERIALIZABLE:不会发生脏读、不可重复读、幻读。

什么是脏读、不可重复读、幻读?

dirty read(脏读):a row changed by one transaction to be read by another transaction before any changes in that row have been committed (a "dirty read"). If any of the changes are rolled back, the second transaction will have retrieved an invalid row.

non-repeatable read(不可重复读):one transaction reads a row,a second transaction alters the row, and the first transaction re-reads the row, getting different values the second time (a "non-repeatable read").

phantom read(幻读):one transaction reads all rows that satisfy a condition(X), a second transaction inserts a row that satisfies that condition(X), and the first transaction re-reads for the same condition, retrieving the additional "phantom" row in the second read.

Propagation

  1. PROPAGATION_REQUIRED:Support a current transaction; create a new one if none exists.
  2. PROPAGATION_SUPPORTS:Support a current transaction; execute non-transactionally if none exists.
  3. PROPAGATION_MANDATORY:Support a current transaction; throw an exception if no current transaction exists.
  4. PROPAGATION_REQUIRES_NEW:Create a new transaction, suspending the current transaction if one exists.
  5. PROPAGATION_NOT_SUPPORTED:Do not support a current transaction; rather always execute non-transactionally.
  6. PROPAGATION_NEVER:Do not support a current transaction; throw an exception if a current transaction exists.
  7. PROPAGATION_NESTED:Execute within a nested transaction if a current transaction exists, behave like PROPAGATION_REQUIRED else.

博主在 Github 上有一个基于 SSM(Spring、Spring MVC、MyBatis)的小项目,部分学习内容会在该项目中使用。

该项目 Spring 相关配置,完全使用基于注解的方式。博主在刚接触各种配置的时候,绕了一些弯路。

对于刚接触这些框架的朋友,该项目或许会有些许帮助。如果在理解该项目时或参考时遇到任何问题,欢迎通过你能找到的任何方式联系博主,非常乐意共同学习。

项目地址为:spittr

如果你喜欢 xml 配置的方式,可参考另外一个项目 seckill 。该项目是博主在慕课网上学习该课程的源代码,项目中没有完全采用基于注解的方式,相比而言,该项目在配置方面更加老道。

原文地址:https://www.cnblogs.com/xjshi/p/7507872.html