Spring:事务

Spring事务管理

Spring不但提供了与底层代码无关的事务抽象,还提供了声明性事务的功能,可以让程序从事务代码中解放出来。

基础知识

JDBC对事务的支持

JDBC2.0中事务只能提交和回滚,在JDBC3.0中引入了Save Point特性。

ThreadLocal

Spring通过各种模板类降低开发者使用各种数据持久化技术的难度,而模板类需要绑定数据连接或会话的资源,这些资源不是线程安全的,即不能在同一时刻被多个线程共享。

Spring使用ThreadLocal实现资源的线程安全,并且在管理request作用域的Bean、事务任务调度、AOP等模块都有应用。

Spring对事务管理的支持

Spring为事务管理提供了一致的编程模板,在高层次建立了统一的事务抽象,即无论是JDBC、,MyBatis都可以让用户用统一的编程模型进行事务管理。

Spring事务管理也提供了事务模板类TransactionTemplate,通过TransactionTemplate并配合使用事务回调TransactionCallback指定具体的持久化操作,就可以通过编程方式实现事务管理,而无须关注资源获取、复用释放、事务同步和异常处理等操作。

Spring事务优势:

  • 声明式事务管理。
    • 允许通过声明方式,在IOC配置中指定事务的边界和事务属性,Spring自动在指定的事务边界上应用事务属性。

事务管理关键抽象

Spring的事务管理的抽象层主要包括3个接口:PlatformTransactionManager、TransactionDefinition、TransactionStatus。

1566717004974

TransactionDefinition描述事务的隔离级别、超时时间、是否为只读事务和事务的传播规则等控制事务具体行为的事务属性。

PlatformTransactionManager根据TransactionDefinition提供的事务属性配置信息创建事务,并用TransactionStatus描述这个激活事务的状态。

TransactionDefinition

  • 事务隔离。定义当前事务与其他事务的隔离程度
    • Isolation_read_uncommited。
    • Isolation_read_commited。
    • Isolation_repeatable_read。
    • Isolation_serializable。
    • Isolation_default。表示运用底层数据库的默认事务隔离级别。
  • 事务传播。通常在一个事务中执行的所有代码都会运行在同一事务上下文当中,但是Spring也提供了几个可选的事务传播类型。
    • 简单地参与到现有的事务中。
    • 挂起当前的事务,创建一个新的事务。等
  • 事务超时。事务在超时前能运行多久,超时后事务被回滚。
  • 只读状态。不修改任何数据,可用针对可读事务应用一些优化措施,提高运行性能。

TransactionStatus

TransactionStatus代表一个事务的具体运行状态,事务管理器可以通过该接口获取事务运行期的状态信息,也可以通过该接口间接地回滚事务。

相比于在抛出异常时回滚事务的方式更具有可控性,该接口继承自SavePointManager接口,其提供的方法有:

  • Object createSavepoint()。创建一个保存点对象,以便在后面可以利用rollbackToSavepoint(Object savepoint)方法使事务回滚到特定的保存点上,也可以通过releaseSavepoint()释放一个已经不用的保存点。
  • void rollbackToSavepoint(Object savepoint)。
  • void releaseSavepoint()。

TransactionStatus扩展支持了以下方法:

  • boolean hasSavepoint()。判断事务释放在内部创建了一个保存点,该保存点是为了支持Spring的嵌套事务而创建的。
  • boolean isNewTransaction()。判断当前事务是否是一个新的事务,如果返回false,则表示事务是一个已经存在的事务,或当前操作未运行在事务环境中。
  • boolean isCompleted()。判断当前事务是否已经结束。
  • void setRollbackOnly()。将当前事务设置未rollback-only,通过该标识通知事务管理器只能将事务回滚,事务管理器将通过显式调用回滚命令或抛出异常来回滚事务。

PlatformTransactionManager

提供了事务管理的抽象。

  • TransactionStatus getTransaction(TransactionDefinition definition)。根据事务定义信息从事务环境中返回一个已存在的事务,或创建一个新的事务。
  • commit(TransactionStatus status)。根据事务的状态提交事务,如果事务状态被设置未rollback-only,则执行回滚事务的操作。
  • rollback(TransactionStatus status)。将事务回滚,当commit抛出异常,则将隐式调用rollback。

事务同步管理器

Spring将JDBC的Connection的Session等访问数据库的连接或会话对象统称为资源,这些资源在同一时刻是不能多线程共享的。

Spring的事务同步管理器类使用ThreadLocal为不同事务线程提供了独立的资源副本,同时维护事务配置的属性和运行状态信息。

1
2
3
4
5
6
7
8
public abstract class TransactionSynchronizationManager{
//用于保存每个事务线程对应的Connection或Session等类型的资源
private static final ThreadLocal resources = new ThreadLocal();
//用于保存每个事务线程对应的事务的名称
private static final ThreadLocal currentTransactionName = new ThreadLocal();
//用于保存每个事务线程对应事务的read-only状态
private static final ThreadLocal currentTransactionReadOnly = new ThreadLocal。
}

TransactionSynchronizationManager将DAO、Service中影响线程安全的所有状态统一抽取到该类当中,并且使用ThreadLocal进行替换,因此它们都将是线程安全的。

事务传播行为

当我们调用一个基于Spring的Service接口方法时,它将运行于Spring管理的事务环境中,Service接口方法可能会在内部调用其他的Service接口方法以共同完成一个完整的业务操作,因此就会产生服务接口方法嵌套调用的情况。

Spring通过事务传播行为控制当前的事务如何传播到被嵌套调用的目标服务接口方法中。

事务传播行为类型

在TransactionDefinition接口中定义,规定了事务方法和事务方法间发生嵌套调用时,事务如何进行传播:

  • Propagation_Required。如果当前没有事务,则新建一个事务,如果已经存在一个事务,则加入到这个事务。
    • 是最常见的选择。
  • Propagation_Supports。支持当前事务,如果当前没有事务,则以非事务方式运行。
  • Propagation_Mandatory。使用当前的事务,如果当前没有事务,则抛出异常。
  • Propagation_Requires_New。新建事务,如果当前存在事务,则将当前事务挂起。
  • Propagation_Not_Supported。以非事务方式执行操作, 如果当前存在事务,则将当前事务挂起。
  • Propagation_Never。以非事务的方式执行,如果当前存在事务,则抛出异常。
  • Propagation_Nested。如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则执行与Propagation_Required类似的操作。
    • 需要JDBC3.0,且数据源支持保存点事务机制。

注解

@Transaction默认属性:

  • 事务传播行为:Progation_Required。
  • 事务隔离级别:Isolation_Default。
  • 读写事务属性:读/写事务。
  • 超时时间:依赖于底层的事务系统的默认值。
  • 回滚设置:任何运行期异常引发回滚,任何检查型异常不会引发回滚。

何处标注

  • 接口定义。
  • 接口方法。
  • 类定义。
  • 类的public方法。

使用不同的事务管理器:

@Transactional(“topic”)。即使用名为topic的事务管理器。

事务管理难点

事务方法嵌套调用

Spring事务传播机制回顾

Spring事务一个被讹传的说法是:一个事务方法不应该调用另一个事务方法,否则将产生两个事务。

Spring独立实现了事务传播行为,而通过事务传播属性,例如Propagation_Required操作,如果多个ServiceX#method1()->Service2#method2()->Service3#method3()则3个服务的3个方法读将通过Spring的事务传播机制工作在同一个事务中。

相互嵌套的服务方法

当UserService@login()调用ScoreService#addScore()则二者分别通过AOP进行了事务增强,使得它们工作在同一事务中:

即原理是通过AOP将代码作为了一个切面,将addScore()插入了login方法当中,从而实现二者共享同一个事务,因此它们最终工作在同一个事务当中。

多线程

在相同线程中进行相互嵌套调用的事务方法工作在相同的事务中。

如果这些相互嵌套调用的方法工作在不同的线程中,则不同线程下的事务方法工作在独立的事务中。它们被ThreadLocal隔离了。

参考