本文为 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 。该项目是博主在慕课网上学习该课程的源代码,项目中没有完全采用基于注解的方式,相比而言,该项目在配置方面更加老道。

Spring 事务管理笔记的更多相关文章

  1. Spring事务管理笔记

    事务的目的就是要保证数据的高度完整性和一致性. 在实际的项目中,大多都是使用注解的方式来实现事物,这里也就简单记录下使用@Transactional方法和注意事项. 在xml中添加配置 1234567 ...

  2. 阿里面试挂了,就因为面试官说我Spring 事务管理(器)不熟练?

    前言 事务管理,一个被说烂的也被看烂的话题,还是八股文中的基础股之一.但除了八股文中需要熟读并背诵的那些个传播行为之外,背后的"为什么"和核心原理更为重要. ​ 写这篇文章之前,我 ...

  3. 【Java EE 学习 52】【Spring学习第四天】【Spring与JDBC】【JdbcTemplate创建的三种方式】【Spring事务管理】【事务中使用dbutils则回滚失败!!!??】

    一.JDBC编程特点 静态代码+动态变量=JDBC编程. 静态代码:比如所有的数据库连接池 都实现了DataSource接口,都实现了Connection接口. 动态变量:用户名.密码.连接的数据库. ...

  4. spring事务管理器设计思想(二)

    上文见<spring事务管理器设计思想(一)> 对于第二个问题,涉及到事务的传播级别,定义如下: PROPAGATION_REQUIRED-- 如果当前没有事务,就新建一个事务.这是最常见 ...

  5. spring事务管理器设计思想(一)

    在最近做的一个项目里面,涉及到多数据源的操作,比较特殊的是,这多个数据库的表结构完全相同,由于我们使用的ibatis框架作为持久化层,为了防止每一个数据源都配置一套规则,所以重新实现了数据源,根据线程 ...

  6. 事务管理(下) 配置spring事务管理的几种方式(声明式事务)

    配置spring事务管理的几种方式(声明式事务) 概要: Spring对编程式事务的支持与EJB有很大的区别.不像EJB和Java事务API(Java Transaction API, JTA)耦合在 ...

  7. Spring事务管理器的应对

    Spring抽象的DAO体系兼容多种数据访问技术,它们各有特色,各有千秋.像Hibernate是非常优秀的ORM实现方案,但对底层SQL的控制不太方便:而iBatis则通过模板化技术让你方便地控制SQ ...

  8. Spring事务管理(转)

    1 初步理解 理解事务之前,先讲一个你日常生活中最常干的事:取钱. 比如你去ATM机取1000块钱,大体有两个步骤:首先输入密码金额,银行卡扣掉1000元钱:然后ATM出1000元钱.这两个步骤必须是 ...

  9. [Spring框架]Spring 事务管理基础入门总结.

    前言:在之前的博客中已经说过了数据库的事务, 不过那里面更多的是说明事务的一些锁机制, 今天来说一下Spring管理事务的一些基础知识. 之前的文章: [数据库事务与锁]详解一: 彻底理解数据库事务一 ...

随机推荐

  1. 关于bootstrap的一些想法

    老实说,作为一个前端人员,我不怎么会去用bootstrap,但是我会去看,会去了解. 首先,bootstrap其实是给后台以及前端新人用来快速完成一个页面的简单布局,不是按照设计稿psd格式给定做的那 ...

  2. 21. leetcode 492

    492: 给定一个面积值,求它的长l和宽w.长和宽需满足:长大于等于宽,长和宽的差值尽可能小,长乘宽等于面积. 思路:先将l和w初始化为sqrt(area),然后看l*w是否等于面积,如果等于则返回l ...

  3. 设置双击直接打开.ipynb文件

    本文环境:win10(64)+anaconda3(64) anaconda3安装好后,可在开始菜单下查看 背景:jupyter notebook打开后无法更改路径,只能在默认路径下upload文件(如 ...

  4. ios-->制作ipa文件

    用证书进行真机调试并生成二级制文件,通常位于:/Users/.../Library/Developer/Xcode/DerivedData/XXXXXDemo-gmtamkryoesxilartayu ...

  5. NYOJ 66 分数拆分

    分数拆分 时间限制:3000 ms  |  内存限制:65535 KB 难度:1   描述 现在输入一个正整数k,找到所有的正整数x>=y,使得1/k=1/x+1/y.   输入 第一行输入一个 ...

  6. NYOJ 128 前缀表达式的计算

    前缀式计算 时间限制:1000 ms  |  内存限制:65535 KB 难度:3   描述 先说明一下什么是中缀式: 如2+(3+4)*5这种我们最常见的式子就是中缀式. 而把中缀式按运算顺序加上括 ...

  7. [学习笔记] 多项式与快速傅里叶变换(FFT)基础

    引入 可能有不少OIer都知道FFT这个神奇的算法, 通过一系列玄学的变化就可以在 $O(nlog(n))$ 的总时间复杂度内计算出两个向量的卷积, 而代码量却非常小. 博主一年半前曾经因COGS的一 ...

  8. C#快速入门

    一.简介 1.C#是由Anders Hejlsberg和他的团队在.Net框架开发期间开发的:是.Net框架的一部分. C#是专为公共语言基础结构(CLI)设计的,CLI由可执行代码和运行时环境组成, ...

  9. 鄙人对constructor和prototype的总结

    在学习js面向对象过程中,我们总是对constructor和prototype充满疑惑,这两个概念是相当重要的,深入理解这两个概念对理解js的一些核心概念非常的重要.因此,在这里记录下鄙人见解,希望可 ...

  10. 扩展Python模块系列(二)----一个简单的例子

    本节使用一个简单的例子引出Python C/C++ API的详细使用方法.针对的是CPython的解释器. 目标:创建一个Python内建模块test,提供一个功能函数distance, 计算空间中两 ...