Spring事务管理

Spring事务概述

JAVA事务局限

  • 局部事务的管理绑定到了具体的数据访问方式

    使用特定的数据访问方式,就必须使用该方式特有的事务管理。其必然结果就是导致业务代码与事务管理代码相互耦合,因为并没有统一所有事务管理API的上层抽象。

  • 事务的异常处理

    与数据访问相同,特定API的事务管理的异常体系并不统一。当使用特定API时,就必须在代码中实现一套异常处理逻辑(而且,同时在事务管理中,往往也要处理数据访问抛出的异常)。相应地,这对未来的维护以及技术的替换带来了前所未有的困难。

  • CMT声明式事务的局限

    声明式事务必然依赖某种容器。而当时企业级应用的容器EJB,才能保证声明式事务的使用。

Spring事务

Spring抽离了事务管理的关注点,并对这些关注点进行了抽象。这样子使得开发人员,在spring容器下也可以使用统一的事务管理编程模型,而不需要关注使用的数据访问方式和数据资源类型。

Spring的事务管理可以良好结合Spring的数据访问。

Spring事务框架设计原则

image.png

Spring事务框架

核心接口

public interface PlatformTransactionManager {

   TransactionStatus getTransaction(TransactionDefinition definition) throws TransactionException;

   void commit(TransactionStatus status) throws TransactionException;

   void rollback(TransactionStatus status) throws TransactionException;

}

作为Spring事务管理框架的核心接口,它只是提供了事务管理的统一抽象,具体的管理方式,则根据数据访问方式以及数据资源类型,来派生具体实现。默认实现由:org.springframework.transaction.jta.JtaTransactionManager(分布式事务)和org.springframework.jdbc.datasource.DataSourceTransactionManager(局部事务)。

问题

思考:如果是JDBC的方式,那么进行事务管理,必须基于Connection(或者hibernate的session)。那么在TransactionManager中是通过什么方式获取的呢。实际上,是与Spring的数据访问相结合,在数据访问框架中,通过DataSourceUtil管理connection。因此本身也是借助数据访问框架来完成资源管理的。

对应hibernate的是SessionFactoryUtils,对应JDO的PermissionManagerFactoryUtils

image.png

核心接口体系

image.png

PlatformTransactionManager负责界定事务边界;TransactionDefinition负责定义事务相关属性,比如事务传播,事务隔离级别,超时时间,是否只读等;TransactionStatus负责事务开启之后到结束的事务状态,可以通过该类对事务进行有限的控制。

TransactionDefinition

事务属性

  • 事务隔离级别(默认采用数据库的默认隔离级别)

    int ISOLATION_DEFAULT = -1;
    // 1:读未提交 问题:脏读,幻读,不可重复读
    int ISOLATION_READ_UNCOMMITTED = Connection.TRANSACTION_READ_UNCOMMITTED;
    // 2:读已提交 问题:幻读,不可重复读
    int ISOLATION_READ_COMMITTED = Connection.TRANSACTION_READ_COMMITTED;
    // 4:可重复读 问题:幻读
    int ISOLATION_REPEATABLE_READ = Connection.TRANSACTION_REPEATABLE_READ;
    // 8:串行化
    int ISOLATION_SERIALIZABLE = Connection.TRANSACTION_SERIALIZABLE;
    

脏读(dirty reads)

一个事务读取了另一个未提交的并行事务写的数据。

不可重复读(non-repeatable reads)

一个事务重新读取前面读取过的数据, 发现该数据已经被另一个已提交的事务修改过。 (针对一条记录)

幻读(phantom read)

一个事务重新执行一个查询,返回一套符合查询条件的行, 发现这些行因为其他最近提交的事务而发生了改变。(针对其他多条记录)

  • 事务传播行为

    int PROPAGATION_REQUIRED = 0;
    
    int PROPAGATION_SUPPORTS = 1;
    
    int PROPAGATION_MANDATORY = 2;
    
    int PROPAGATION_REQUIRES_NEW = 3;
    
    int PROPAGATION_NOT_SUPPORTED = 4;
    
    int PROPAGATION_NEVER = 5;
    
    int PROPAGATION_NESTED = 6;
    
    事务传播行为类型 说明
    PROPAGATION_REQUIRED 如果当前没有事务,就新建一个事务,如果已经存在一个事务中,加入到这个事务中。这是最常见的选择。
    PROPAGATION_SUPPORTS 支持当前事务,如果当前没有事务,就以非事务方式执行。
    PROPAGATION_MANDATORY 使用当前的事务,如果当前没有事务,就抛出异常。
    PROPAGATION_REQUIRES_NEW 新建事务,如果当前存在事务,把当前事务挂起。
    PROPAGATION_NOT_SUPPORTED 以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。
    PROPAGATION_NEVER 以非事务方式执行,如果当前存在事务,则抛出异常。
    PROPAGATION_NESTED 如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则执行与PROPAGATION_REQUIRED类似的操作

    Spring事务传播行为详解:代码举例清晰

  • 事务超时时间(默认是事务系统的默认超时时间)

    int TIMEOUT_DEFAULT = -1;

  • 是否为只读事务

    由具体实现类控制。

实现类

可以将TransactionDefinition的实现类根据编程式事务场景和声明式事务场景(基于AOP)分为两类。这种分类只是出于每个类在相应场景的出现频率考虑的,并不代表只能在规定场景使用。

image.png
编程式事务

DefaultTransactionDefinitionTransactionDefinition的默认实现类,提供了各种事务属性的默认值,并提供了setter方法以进行更改。默认属性:

private int propagationBehavior = PROPAGATION_REQUIRED;

private int isolationLevel = ISOLATION_DEFAULT;

private int timeout = TIMEOUT_DEFAULT;

private boolean readOnly = false;

TransactionTemplate是Spring提供的简化编程式事务界限和事务异常控制的模板方法类,直接继承自DefaultTransactionDefinition。因此允许通过TransactionTemplate提供事务属性控制。其核心方法execute(实现自TransactionCallback)支持事务代码。

声明式事务

TransactionAttribute继承自TransactionDefinition,主要面向使用Spring AOP进行声明式事务的场景。所以在父接口的基础上添加了一个boolean rollbackOn(Throwable ex);的方法定义,以便通过声明的方式指定业务方法在抛出什么异常时进行回滚。

DelegatingTransactionDefinition是一个抽象类,它将所有事务属性getter方法调用委派给另一个TransactionDefinition的具体实现类。

DelegatingTransactionAttribute依旧是一个抽象类,在DelegatingTransactionDefinition的基础上,将rollbackOn等动作也委派给其他的具体实现类。

DefaultTransactionAttributeTransactionAttribute的通用实现,同时继承了DefaultTransactionDefinition,在此基础上指定了当异常类型为unchecked exception的时候进行事务回滚(ex instanceof RuntimeException || ex instanceof Error)。

RuleBasedTransactionAttribute允许指定多个回滚规则,这些规则以RollbackRuleAttributeNoRollbackRuleAttribute的list形式存在。rollbackOn将传入的异常与这些规则匹配,以决定是否进行回滚。

TransactionStatus

image.png

TransactionStatus代表着事务状态,程序可以通过该类获取状态信息以及编程式地进行回滚(而不是通过抛出异常)。继承SavepointManager接口提供了SavePoint管理能力。不过该能力只在基础事务管理下可用。

![image.png](https://upload-images.jianshu.io/upload_images/19423360-6a4c2ab5af8e870f.jpg?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

PlatformTransactionManager

PlatformTransactionManager的抽象体系基于strategy模式,由PlatformTransactionManager对事务界定进行统一抽象,而具体的界定策略由具体实现类定义。

由于PlatformTransactionManager的子类在实现时,基本上遵循统一的结构和理念,所以代码以DataSourceTransactionManager为例进行描述。

image.png

相关概念

image.png

AbstractPlatformTransactionManager

AbstractPlatformTransactionManager作为PlatformTransactionManager的抽象实现,以模板方法的封装了固定的事务处理逻辑,而只将与事务资源相关的操作以protected或者abstract方法的形式留给具体的实现类。

而作为抽象模板类,AbstractPlatformTransactionManager替子类实现了以下逻辑:

  • 判断当前是否存在事务,根据判断结果执行不同处理;
  • 根据是否存在当前事务的情况,应用适当的事务传播行为;
  • 挂起以及恢复事务;
  • 提交时检查rollbackonly标志,如果是,则执行事务回滚,而不是提交。
  • 事务回滚的情况下,清理并恢复事务状态;
  • 如果 Transaction Synchronization 为active,则在事务处理的规定时点触发注册的Synchronization回调接口。

主要体现在以下几个模板方法:

public final TransactionStatus getTransaction(@Nullable TransactionDefinition definition) throws TransactionException {}

public final void rollback(TransactionStatus status) throws TransactionException {}

public final void commit(TransactionStatus status) throws TransactionException {}

protected final SuspendedResourcesHolder suspend(@Nullable Object transaction) throws TransactionException {}

protected final void resume(@Nullable Object transaction, @Nullable SuspendedResourcesHolder resourcesHolder) throws TransactionException {}
getTransaction
public final TransactionStatus getTransaction(@Nullable TransactionDefinition definition) throws TransactionException {
    // 1.doGetTransaction是由具体实现类提供的,会返回特定实现的Transaction Object。比如DataSourceTransactionObject。
   // 在doGetTransaction中,会从TransactionSynchronizationManager获取DataSource并绑定到Transaction Object中返回。
   Object transaction = doGetTransaction();

   // Cache debug flag to avoid repeated checks.
   boolean debugEnabled = logger.isDebugEnabled();

   if (definition == null) {
      // 2.Use defaults if no transaction definition given.
      // 如果没有提供 Transaction Definition,则生成一个默认的事务属性信息。
      definition = new DefaultTransactionDefinition();
   }
        // 3.检测是否存在事务,再根据事务传播行为进行对应的处理。
    // isExistingTransaction 默认返回false,具体实现类根据对应的Transaction Object进行判断(内部封装的DataSource)。
   if (isExistingTransaction(transaction)) {
      // 4.Existing transaction found -> check propagation behavior to find out how to behave.
      return handleExistingTransaction(definition, transaction, debugEnabled);
   }

   // Check definition settings for new transaction.
   if (definition.getTimeout() < TransactionDefinition.TIMEOUT_DEFAULT) {
      throw new InvalidTimeoutException("Invalid transaction timeout", definition.getTimeout());
   }
   // 根据事务传播行为,创建新的事务
   // No existing transaction found -> check propagation behavior to find out how to proceed.
   if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_MANDATORY) {
      throw new IllegalTransactionStateException(
            "No existing transaction found for transaction marked with propagation 'mandatory'");
   }
   else if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRED ||
         definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRES_NEW ||
         definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NESTED) {
      SuspendedResourcesHolder suspendedResources = suspend(null);
      if (debugEnabled) {
         logger.debug("Creating new transaction with name [" + definition.getName() + "]: " + definition);
      }
      try {
         boolean newSynchronization = (getTransactionSynchronization() != SYNCHRONIZATION_NEVER);
         DefaultTransactionStatus status = newTransactionStatus(
               definition, transaction, true, newSynchronization, debugEnabled, suspendedResources);
         doBegin(transaction, definition);
         prepareSynchronization(status, definition);
         return status;
      }
      catch (RuntimeException | Error ex) {
         resume(null, suspendedResources);
         throw ex;
      }
   }
   else {
      // Create "empty" transaction: no actual transaction, but potentially synchronization.
      if (definition.getIsolationLevel() != TransactionDefinition.ISOLATION_DEFAULT && logger.isWarnEnabled()) {
         logger.warn("Custom isolation level specified but no actual transaction initiated; " +
               "isolation level will effectively be ignored: " + definition);
      }
      boolean newSynchronization = (getTransactionSynchronization() == SYNCHRONIZATION_ALWAYS);
      return prepareTransactionStatus(definition, null, true, newSynchronization, debugEnabled, null);
   }
}
3.isExistingTransaction & handleExistingTransaction

举例一下DataSourceTransactionManager的实现。

基于JDBC的实现,查看JDBC Connection是否存在并活跃。

protected boolean isExistingTransaction(Object transaction) {
   DataSourceTransactionObject txObject = (DataSourceTransactionObject) transaction;
   return (txObject.hasConnectionHolder() && txObject.getConnectionHolder().isTransactionActive());
}

默认实现,私有方法。handleExistingTransaction根据事务传播行为,处理已存在的事务。

suspend&resume

这两个方法只是对TransactionSynchronizationManager中当前事务资源进行处理。

suspend是将TransactionSynchronizationManager中的资源全部取出,封装SuspendedResourcesHolder中。然后将TransactionSynchronizationManager清空。返回SuspendedResourcesHolderTransactionStatus会保存SuspendedResourcesHolder,以便事后释放该挂起的事务。

resume则是suspend的相反操作。将从TransactionStatus中获取的SuspendedResourcesHolder,重新放回到TransactionSynchronizationManager中。

suspend

@Nullable
    protected final SuspendedResourcesHolder suspend(@Nullable Object transaction) throws TransactionException {
        if (TransactionSynchronizationManager.isSynchronizationActive()) {
            List suspendedSynchronizations = doSuspendSynchronization();
            try {
                Object suspendedResources = null;
                if (transaction != null) {
          // 子类实现,DataSourceTransactionManager则释放Connection&DataSource
                    suspendedResources = doSuspend(transaction);
                }
                String name = TransactionSynchronizationManager.getCurrentTransactionName();
                TransactionSynchronizationManager.setCurrentTransactionName(null);
                boolean readOnly = TransactionSynchronizationManager.isCurrentTransactionReadOnly();
                TransactionSynchronizationManager.setCurrentTransactionReadOnly(false);
                Integer isolationLevel = TransactionSynchronizationManager.getCurrentTransactionIsolationLevel();
                TransactionSynchronizationManager.setCurrentTransactionIsolationLevel(null);
                boolean wasActive = TransactionSynchronizationManager.isActualTransactionActive();
                TransactionSynchronizationManager.setActualTransactionActive(false);
                return new SuspendedResourcesHolder(
                        suspendedResources, suspendedSynchronizations, name, readOnly, isolationLevel, wasActive);
            }
        }
        else if (transaction != null) {
            // Transaction active but no synchronization active.
            Object suspendedResources = doSuspend(transaction);
            return new SuspendedResourcesHolder(suspendedResources);
        }
        else {
            // Neither transaction nor synchronization active.
            return null;
        }
    }
private List doSuspendSynchronization() {
        List suspendedSynchronizations =
                TransactionSynchronizationManager.getSynchronizations();
        for (TransactionSynchronization synchronization : suspendedSynchronizations) {
            synchronization.suspend();
        }
        TransactionSynchronizationManager.clearSynchronization();
        return suspendedSynchronizations;
    }

resume

protected final void resume(@Nullable Object transaction, @Nullable SuspendedResourcesHolder resourcesHolder)
  throws TransactionException {

  if (resourcesHolder != null) {
    Object suspendedResources = resourcesHolder.suspendedResources;
    if (suspendedResources != null) {
      doResume(transaction, suspendedResources);
    }
    List suspendedSynchronizations = resourcesHolder.suspendedSynchronizations;
    if (suspendedSynchronizations != null) {
      TransactionSynchronizationManager.setActualTransactionActive(resourcesHolder.wasActive);
      TransactionSynchronizationManager.setCurrentTransactionIsolationLevel(resourcesHolder.isolationLevel);
      TransactionSynchronizationManager.setCurrentTransactionReadOnly(resourcesHolder.readOnly);
      TransactionSynchronizationManager.setCurrentTransactionName(resourcesHolder.name);
      doResumeSynchronization(suspendedSynchronizations);
    }
  }
}

private void doResumeSynchronization(List suspendedSynchronizations) {
        TransactionSynchronizationManager.initSynchronization();
        for (TransactionSynchronization synchronization : suspendedSynchronizations) {
            synchronization.resume();
            TransactionSynchronizationManager.registerSynchronization(synchronization);
        }
    }
commit&rollback

这两个方法都会去检查isRollbackOnly标志的值,而采取相应的操作(回滚或提交)。

主要针对savepoint的处理(嵌套事务)和当前事务的回滚或提交、TransactionSynchronization的调用、资源清理。

引用
《spring揭秘》

你可能感兴趣的:(Spring事务管理)