Spring 事务第二篇-Spring 事务源码解析之事务管理器

目录

前言

事务管理器-PlatformTransactionManager

DataSourceTransactionManager

AbstractPlatformTransactionManager

获取事务状态 getTransaction

提交事务 commit        

回滚事务 rollback

总结


​​​​​​​

前言

        相信看了前面的文章,已经知道怎么怎么使用 java 操作数据库,怎么使用事务来管理多个数据库操作。在会使用之后自然是要继续研究一下,Spring  事务到底是如何实现的呢?不同传播类型的事务是怎么管理的呢?事务怎么管理数据库连接呢?又是怎么提交和回滚的呢?本文主要就是探讨这些问题。

事务管理器-PlatformTransactionManager

        还记得上篇文章讲解事务使用的时候都是进行了什么配置吗?首先是数据源 DataSource,它用来保存管理数据库连接,其次是 JdbcTemplate, 可以通过它来进行数据库操作,最后一项便是 PlatformTransactionManager 事务管理器,它就是用来管理事务的。配置类如下所示

@EnableTransactionManagement
@ComponentScan("com.transaction.*")
public class TransactionConfig {

    @Bean
    public DataSource dataSource() {
        DruidDataSource dataSource = new DruidDataSource();
        dataSource.setUsername("root");
        dataSource.setPassword("123456");
        dataSource.setUrl("jdbc:mysql://localhost:3306/transaction?useSSL=false");
        dataSource.setDriverClassName("com.mysql.jdbc.Driver");
        dataSource.setMinIdle(5);
        dataSource.setMaxActive(20);
        dataSource.setInitialSize(10);
        return dataSource;
    }

    @Bean
    public JdbcTemplate jdbcTemplate(DataSource dataSource) {
        return new JdbcTemplate(dataSource);
    }

    @Bean
    public PlatformTransactionManager transactionManager(DataSource dataSource) {
        //调用事务管理器构造方法时需要传入 dataSource 实例,以便事务管理器实现对数据库连接的间接管理。
        return new DataSourceTransactionManager(dataSource);
    }
}

        前两项在前面的文章已经都剖析过了,最后这一项才是本文的重点跟踪对象。Spring 就是通过它来管理事务的。首先看一下 PlatformTransactionManager 的信息。

public interface PlatformTransactionManager extends TransactionManager {

	//获取事务
	TransactionStatus getTransaction(@Nullable TransactionDefinition definition)
			throws TransactionException;

	//提交事务
	void commit(TransactionStatus status) throws TransactionException;

	//回滚事务
	void rollback(TransactionStatus status) throws TransactionException;

}

        由上所示,它是一个接口,然后提供了三个接口方法,分别是获取事务,提交事务和回滚事务。相当于是提出来了事务管理器的规范,具体怎么实现交由它的实现类来完成。所以我们紧接着需要研究一下 DataSourceTransactionManager 这个实现类,它也是经常被用来管理事务的管理器。

        首先分析一下 getTransaction 方法的 TransactionDefinition 类型的参数,它用来保存一些事务隔离级别,事务传播行为,超时时间等信息。事务管理器可以根据这些事务信息来获取一个已存在或者创建一个新的事务,但是这些信息都是针对于一个新事务的,如果当前事务融合到另外一个事务中去了。那么当前的事务信息就会失效了。

        再分析一下 commit 方法的 TransactionStatus 类型的参数。TransactionStatus 保存着事务的状态。在不同时刻有着不同的状态。Spring 根据可以根据这个实例来掌握是否是新的事务,是否可以回滚等信息。

        rollback 也是根据 TransactionStatus 来进行回滚,不过,无论 commit 是成功还是失败,都不能在调用 commit 之后在进行回滚操作。因为 commit 执行之后,事务就已经被清理干净了。

        注意 getTransaction 方法的返回值就是事务提交或者回滚所需要的 TransactionStatus 类型,所以 getTransaction 方法的作用不只是搞一个事务实例出来,还依据 TransactionDefinition 配置的信息给丰富上了,此外还有运行状态信息,比如当前是新事务还是内部事务,是否有保存点等信息。

DataSourceTransactionManager

        首先我们知道事务的提交和回滚都是依赖数据库连接的,但是数据库连接又是 DataSource 管理的,所以 DataSourceTransactionManager 也需要依赖 DataSource, 这也就是构造 DataSourceTransactionManager 的时候需要传入 DataSource 的原因。首先看一下构造方法

    public DataSourceTransactionManager(DataSource dataSource) {
        // 调用无参构造方法
		this();
        //给本地变量 DataSource 赋值,用来实现对数据库连接的间接操作。
		setDataSource(dataSource);
        
		afterPropertiesSet();
	}

    public DataSourceTransactionManager() {
        //设置允许 NESTED 类型的事务传播方式
		setNestedTransactionAllowed(true);
	}

    public void afterPropertiesSet() {
        //判断是否已经配置好了数据源
		if (getDataSource() == null) {
			throw new IllegalArgumentException("Property 'dataSource' is required");
		}
	}

        通过上面的几个方法可以看到,调用构造方

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

		// Use defaults if no transaction definition given.
		TransactionDefinition def = (definition != null ? definition : TransactionDefinition.withDefaults());

		Object transaction = doGetTransaction();
		boolean debugEnabled = logger.isDebugEnabled();

		if (isExistingTransaction(transaction)) {
			// Existing transaction found -> check propagation behavior to find out how to behave.
			return handleExistingTransaction(def, transaction, debugEnabled);
		}

		// Check definition settings for new transaction.
		if (def.getTimeout() < TransactionDefinition.TIMEOUT_DEFAULT) {
			throw new InvalidTimeoutException("Invalid transaction timeout", def.getTimeout());
		}

		// No existing transaction found -> check propagation behavior to find out how to proceed.
		if (def.getPropagationBehavior() == TransactionDefinition.PROPAGATION_MANDATORY) {
			throw new IllegalTransactionStateException(
					"No existing transaction found for transaction marked with propagation 'mandatory'");
		}
		else if (def.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRED ||
				def.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRES_NEW ||
				def.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NESTED) {
            //因为没有外部事务,所以不需要挂起什么,故参数为 null
			SuspendedResourcesHolder suspendedResources = suspend(null);
			if (debugEnabled) {
				logger.debug("Creating new transaction with name [" + def.getName() + "]: " + def);
			}
			try {
				return startTransaction(def, transaction, debugEnabled, suspendedResources);
			}
			catch (RuntimeException | Error ex) {
				resume(null, suspendedResources);
				throw ex;
			}
		}
		else {
			// Create "empty" transaction: no actual transaction, but potentially synchronization.
			if (def.getIsolationLevel() != TransactionDefinition.ISOLATION_DEFAULT && logger.isWarnEnabled()) {
				logger.warn("Custom isolation level specified but no actual transaction initiated; " +
						"isolation level will effectively be ignored: " + def);
			}
			boolean newSynchronization = (getTransactionSynchronization() == SYNCHRONIZATION_ALWAYS);
			return prepareTransactionStatus(def, null, true, newSynchronization, debugEnabled, null);
		}
	}

法会注入 DataSource,然后还会设置允许使用 NESTED 类型的数据传播方式。此外,如果没有设置 DataSource 的话还会报错。

        前面我们说了事务管理器的三个主要方法,获取事务,提交事务,回滚事务。但是在这个实现类里并没有找到这三个方法的具体实现。所以说,一定是由它的父类实现的。我们继续看一下它的父类 AbstractPlatformTransactionManager。

AbstractPlatformTransactionManager

        AbstractPlatformTransactionManager 是一个抽象类,它是 DataSourceTransactionManager 的父亲,是 PlatformTransactionManager 的孩子,所以它实现了 PlatformTransactionManager 的三个方法,那么 DataSourceTransactionManager 就直接用就好了。这就是当儿子的好处呀。

        我们按照 获取事务状态,提交事务,回滚事务这样的顺序来看一下代码层面的具体实现。首先是获取事务

获取事务状态 getTransaction

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

		// Use defaults if no transaction definition given.
        //获取事务的配置信息,如果外部只是使用了 @Transactional, 但是并没有配置任何属性信息,那么就会全部使用默认值,比如超时时间 -1,事务传播方式为 Required 
		TransactionDefinition def = (definition != null ? definition : TransactionDefinition.withDefaults());
        //获取事务对象
		Object transaction = doGetTransaction();
		boolean debugEnabled = logger.isDebugEnabled();
        //判断事务是否已经存在,如果外部事务执行 handleExistingTransaction 的逻辑,否则继续往下走。
		if (isExistingTransaction(transaction)) {
			// Existing transaction found -> check propagation behavior to find out how to behave.
			return handleExistingTransaction(def, transaction, debugEnabled);
		}

        //如果走到这里说明不存在外部事务,下面的逻辑的前提也就是没有外部事务的存在
		// Check definition settings for new transaction.
        //如果配置的超时时间比默认的 -1 还要小则会抛出异常
		if (def.getTimeout() < TransactionDefinition.TIMEOUT_DEFAULT) {
			throw new InvalidTimeoutException("Invalid transaction timeout", def.getTimeout());
		}

		// No existing transaction found -> check propagation behavior to find out how to proceed.
        //如果当前事务传播行为是 MANDATORY, 但是又没有外部事务,则会报错。可以看上一篇介绍事务传播特性那一块内容。MANDATORY 是强制必须有外部事务的。
		if (def.getPropagationBehavior() == TransactionDefinition.PROPAGATION_MANDATORY) {
			throw new IllegalTransactionStateException(
					"No existing transaction found for transaction marked with propagation 'mandatory'");
		}
		else if (def.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRED ||
				def.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRES_NEW ||
				def.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NESTED) {
            // 因为不存在外部事务,
			SuspendedResourcesHolder suspendedResources = suspend(null);
			if (debugEnabled) {
				logger.debug("Creating new transaction with name [" + def.getName() + "]: " + def);
			}
			try {
                //这三种事务传播特性表示需要开启一个新事物
				return startTransaction(def, transaction, debugEnabled, suspendedResources);
			}
			catch (RuntimeException | Error ex) {
				resume(null, suspendedResources);
				throw ex;
			}
		}
		else {
            // 其他三种事务传播类型 SUPPORT, NOT_SUPPORTED,NEVER 会返回一个空的事务
			// Create "empty" transaction: no actual transaction, but potentially synchronization.
			if (def.getIsolationLevel() != TransactionDefinition.ISOLATION_DEFAULT && logger.isWarnEnabled()) {
				logger.warn("Custom isolation level specified but no actual transaction initiated; " +
						"isolation level will effectively be ignored: " + def);
			}
			boolean newSynchronization = (getTransactionSynchronization() == SYNCHRONIZATION_ALWAYS);
			return prepareTransactionStatus(def, null, true, newSynchronization, debugEnabled, null);
		}
	}

        通过上面的代码,我们可以知道 getTransaction 方法有三块内容比较重要,1 是 doGetTransaction() 获取事务对象。2 是 handleExistingTransaction,即当前事务被外部事务调用时应该怎么做? 3 是 startTransaction 方法,即如何创建一个新事务。

        首先我们先看一下获取事务对象的方法 doGetTransaction(), 该方法在当前抽象类中只是一个抽象方法,并没有具体实现,所以它的实现一定在它的孩子 DataSourceTransactionManager 那里,所以我们需要看 DataSourceTransactionManager 的 doGetTransaction() 方法

    protected Object doGetTransaction() {
        // 初始化一个对象实例出来
		DataSourceTransactionObject txObject = new DataSourceTransactionObject();
        // 设置是否可以设置保存点(刚刚我们分析过,通过我们本例的方法调用构造函数时会设置为true)
		txObject.setSavepointAllowed(isNestedTransactionAllowed());
        // 数据库连接持有器, 可以看到 obtainDataSource()方法,它是返回我们前面配置的 dataSource, 所以是通过 dataSource 获取到数据库连接的相关信息的
		ConnectionHolder conHolder =
				(ConnectionHolder) TransactionSynchronizationManager.getResource(obtainDataSource());
		txObject.setConnectionHolder(conHolder, false);
		return txObject;
	}

    public static Object getResource(Object key) {
        //方法传进来的key就是dataSource实例对象。
        // 这个方法的意义是判断 dataSource 是不是被代理了,如果被代理了解析出原生的 dataSource
		Object actualKey = TransactionSynchronizationUtils.unwrapResourceIfNecessary(key);
        //还是通过dataSource信息去获取数据库连接相关信息。
		Object value = doGetResource(actualKey);
		if (value != null && logger.isTraceEnabled()) {
			logger.trace("Retrieved value [" + value + "] for key [" + actualKey + "] bound to thread [" +
					Thread.currentThread().getName() + "]");
		}
		return value;
	}

    private static Object doGetResource(Object actualKey) {
        // 从本地线程缓存中获取,如果获取不到直接返回null
		Map<Object, Object> map = resources.get();
		if (map == null) {
			return null;
		}
        //如果缓存中存在,则从缓存中获取数据库连接的信息
		Object value = map.get(actualKey);
		// Transparently remove ResourceHolder that was marked as void...
		if (value instanceof ResourceHolder && ((ResourceHolder) value).isVoid()) {
			map.remove(actualKey);
			// Remove entire ThreadLocal if empty...
			if (map.isEmpty()) {
				resources.remove();
			}
			value = null;
		}
		return value;
	}

        通过上面的分析,我们知道 doGetTransaction 大概是这么一个流程,首先实例化一个事务对象,然后获取数据库连接信息。获取数据库连接的时候会从缓存中获取,如果缓存中没有的话就获取不到。那么我们现在分析一下,doGetTransaction 是当前创建事务的第一步,还没有数据库连接什么事呢,也没有往缓存里存什么东西,所以说在这一步就根本获取不到数据库连接的相关信息的。所以我们目前只是获取到了一个实例化的事务对象。

        然后我们再研究一下 startTransaction 开启新事务的方法,为什么要先看这个呢,因为如果都没有新建过事务的话,那根本就不会有内部事务的存在,就根本不会进入处理 handleExistingTransaction 的逻辑,所以我们先分析 startTransaction,再分析 handleExistingTransaction。

    private TransactionStatus startTransaction(TransactionDefinition definition, Object transaction,
			boolean debugEnabled, @Nullable SuspendedResourcesHolder suspendedResources) {
      
		boolean newSynchronization = (getTransactionSynchronization() != SYNCHRONIZATION_NEVER);
        //通过参数实例化 DefaultTransactionStatus对象,第三个参数标记当前事务为新建事务
		DefaultTransactionStatus status = newTransactionStatus(
				definition, transaction, true, newSynchronization, debugEnabled, suspendedResources);
        //开启事务,这是核心内容
		doBegin(transaction, definition);
        //事务同步器的逻辑
		prepareSynchronization(status, definition);
		return status;
	}

    

         doBegin 方法是开启事务的核心方法,它跟 doGetTransaction 一样,也是由父类声明抽象方法,孩子来实现,所以看一下 DataSourceTransactionManager.doBegin 方法

protected void doBegin(Object transaction, TransactionDefinition definition) {
        //将 Object 类型的事务转换为 DataSourceTransactionObject 类型。
		DataSourceTransactionObject txObject = (DataSourceTransactionObject) transaction;
        //声明数据库连接对象
		Connection con = null;

		try {
            //判断是否已经持有了数据库连接
			if (!txObject.hasConnectionHolder() ||
					txObject.getConnectionHolder().isSynchronizedWithTransaction()) {
                //获取一个数据库连接。这里之前 datasource那里详细讲过
				Connection newCon = obtainDataSource().getConnection();
				if (logger.isDebugEnabled()) {
					logger.debug("Acquired Connection [" + newCon + "] for JDBC transaction");
				}
                //实例化数据库连接包装类,即给数据库连接包一层皮,第二个参数标记为新建的数据库连接持有器
				txObject.setConnectionHolder(new ConnectionHolder(newCon), true);
			}

			txObject.getConnectionHolder().setSynchronizedWithTransaction(true);
			con = txObject.getConnectionHolder().getConnection();
            //如果有外部事务的话,获取外部事务的隔离级别
			Integer previousIsolationLevel = DataSourceUtils.prepareConnectionForTransaction(con, definition);
            //设置外部事务的隔离级别
			txObject.setPreviousIsolationLevel(previousIsolationLevel);
			txObject.setReadOnly(definition.isReadOnly());

			// Switch to manual commit if necessary. This is very expensive in some JDBC drivers,
			// so we don't want to do it unnecessarily (for example if we've explicitly
			// configured the connection pool to set it already).
            // 如果当前连接是自动提交的话,需要设置为非自动提交
			if (con.getAutoCommit()) {
				txObject.setMustRestoreAutoCommit(true);
				if (logger.isDebugEnabled()) {
					logger.debug("Switching JDBC Connection [" + con + "] to manual commit");
				}
				con.setAutoCommit(false);
			}
            //这里一种优化,如果事务设置了readOnly, 这里可以执行 sql语句提示数据库当前事务只会进行读取操作。这样可以提高事务的效率,有兴趣的可以看下这个方法,主要是执行了stmt.executeUpdate("SET TRANSACTION READ ONLY");
			prepareTransactionalConnection(con, definition);
            //通过数据库连接持有器设置当前的事务状态为正在活动中。
			txObject.getConnectionHolder().setTransactionActive(true);
            //如果配置了超时时间,也要在这里通过数据库连接器设置好
			int timeout = determineTimeout(definition);
			if (timeout != TransactionDefinition.TIMEOUT_DEFAULT) {
				txObject.getConnectionHolder().setTimeoutInSeconds(timeout);
			}

			// Bind the connection holder to the thread.
            // 如果当前数据库连接器是新建的,即首次获取数据库连接,需要调用绑定资源bindResource方法。
			if (txObject.isNewConnectionHolder()) {
				TransactionSynchronizationManager.bindResource(obtainDataSource(), txObject.getConnectionHolder());
			}
		}

		catch (Throwable ex) {
			if (txObject.isNewConnectionHolder()) {
				DataSourceUtils.releaseConnection(con, obtainDataSource());
				txObject.setConnectionHolder(null, false);
			}
			throw new CannotCreateTransactionException("Could not open JDBC Connection for transaction", ex);
		}
	}

        该方法首先是要获取到数据库连接,获取方式就是使用dataSource, 获取到连接之后设置 AutoCommit 为 false, 如果当前事务被外部事务包裹,且事务的隔离级别不同的话,需要设置当前的事务隔离级别为当前事务的级别,同时将外层事务隔离级别查出来保存。然后设置 transactionActive 字段为true, 标识当前事务正在活动中。最后如果数据库连接是刚创建出来的。需要将获取到的数据库连接绑定与当前数据源绑定。下面分析一下 bindResource 方法。

    //该方法的 key 为数据源,value 为 数据库连接持有器
    public static void bindResource(Object key, Object value) throws IllegalStateException {
        // 通过数据源获取 actualKey, 上面已经分析过。可以理解为它就是 dataSource 实例
		Object actualKey = TransactionSynchronizationUtils.unwrapResourceIfNecessary(key);
		Assert.notNull(value, "Value must not be null");
        //从本地线程缓存中获取
		Map<Object, Object> map = resources.get();
		// set ThreadLocal Map if none found
		if (map == null) {
            // 如果缓存中没有
			map = new HashMap<>();
            //将 map 缓存进本地 ThreadLocal 变量中。
			resources.set(map);
		}
        //将数据库连接和数据源缓存起来
		Object oldValue = map.put(actualKey, value);
		// Transparently suppress a ResourceHolder that was marked as void...
		if (oldValue instanceof ResourceHolder && ((ResourceHolder) oldValue).isVoid()) {
			oldValue = null;
		}
		if (oldValue != null) {
			throw new IllegalStateException("Already value [" + oldValue + "] for key [" +
					actualKey + "] bound to thread [" + Thread.currentThread().getName() + "]");
		}
		if (logger.isTraceEnabled()) {
			logger.trace("Bound value [" + value + "] for key [" + actualKey + "] to thread [" +
					Thread.currentThread().getName() + "]");
		}
	}

        从上面可以看出,这个方法的主要目的是将数据库连接和数据源缓存在 ThreadLocal 类型变量中,可以理解为当前线程一个数据源只能保存保存着一个数据库连接。还记得我们上面分析的获取事务对象的 doGetTransaction 方法吗?在创建事务的时候就会尝试从缓存中获取数据库连接。如果是同一个线程,同一个数据源,那么就可以获取到同一个数据库连接。这也就说明了当外部事务调用内部事务的场景下,在创建外部事务的时候,在创建事务那一步获取不到数据库连接,知道开启事务 doBegin 的时候才获取到了一个数据库连接出来,然后放入缓存中。紧接着在创建内部事务的时候,在创建事务对象的时候就可以从缓存中获取到数据库连接了。这也就说明,当多个事务嵌套调用的时候,事务对象有多个,但是数据库连接只有一个。

        这样以来 getTransaction 的 doGetTransaction 和 startTransaction 就聊完了,其中第一个方法是用来获取事务对象(如果是内部事务也能获取到数据库连接),第二个方法是用来获取事务状态,也就是 getTransaction 的返回值,这里封装好事务的隔离级别,传播特性,是否只读,当然最重要的是封装数据库连接(如果前面创建事务的时候没有在缓存中获取到,就在这一步通过dataSource获取数据库连接),其实这两步已经获取到事务的实例信息和事务状态。外部事务的执行流程就结束了。

        最后只剩下内部事务的逻辑了。也就是 handleExistingTransaction,当内部事务调用 getTransaction 获取事务状态信息的时候,调用 isExistingTransaction 方法的返回值为 true, 进而会进入 handleExistingTransaction 的逻辑,首先看一下 isExistingTransaction。

protected boolean isExistingTransaction(Object transaction) {
		DataSourceTransactionObject txObject = (DataSourceTransactionObject) transaction;
        //如果事务对象有数据库连接持有器并且transactionActive为true, 则代表存在外部事务
		return (txObject.hasConnectionHolder() && txObject.getConnectionHolder().isTransactionActive());
	}

         外部事务调用 doBegin 方法的时候,会设置 transactionActive 为 true, 上面已经分析过。所以当内部事务通过缓存获取到的数据库连接持有器的该字段确实是 true。接着分析 handleExistingTransaction。

    // 该方法是处理内部事务的逻辑
    private TransactionStatus handleExistingTransaction(
			TransactionDefinition definition, Object transaction, boolean debugEnabled)
			throws TransactionException {
        // 如果事务传播类型为 PROPAGATION_NEVER, 则会直接报错
		if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NEVER) {
			throw new IllegalTransactionStateException(
					"Existing transaction found for transaction marked with propagation 'never'");
		}

        // 如果是 PROPAGATION_NOT_SUPPORTED 类型则会挂起当前事务
		if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NOT_SUPPORTED) {
			if (debugEnabled) {
				logger.debug("Suspending current transaction");
			}
			Object suspendedResources = suspend(transaction);
			boolean newSynchronization = (getTransactionSynchronization() == SYNCHRONIZATION_ALWAYS);
			return prepareTransactionStatus(
					definition, null, false, newSynchronization, debugEnabled, suspendedResources);
		}
        // PROPAGATION_REQUIRES_NEW 类型,则会挂起当前事务,开启一个新的事务
		if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRES_NEW) {
			if (debugEnabled) {
				logger.debug("Suspending current transaction, creating new transaction with name [" +
						definition.getName() + "]");
			}
            //挂起当前事务,获取到数据库连接持有器,这一步主要是将数据库连接从缓存中清除
			SuspendedResourcesHolder suspendedResources = suspend(transaction);
			try {
				return startTransaction(definition, transaction, debugEnabled, suspendedResources);
			}
			catch (RuntimeException | Error beginEx) {
                //当发生异常的时候再将数据库连接绑定到缓存中,下面还是用的 bindResource 方法
				resumeAfterBeginException(transaction, suspendedResources, beginEx);
				throw beginEx;
			}
		}
        // PROPAGATION_NESTED 则会设置一个保存点,以将内部事务和外部事务分开,内部事务回滚时不会影响到外部事务。
		if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NESTED) {
			if (!isNestedTransactionAllowed()) {
				throw new NestedTransactionNotSupportedException(
						"Transaction manager does not allow nested transactions by default - " +
						"specify 'nestedTransactionAllowed' property with value 'true'");
			}
			if (debugEnabled) {
				logger.debug("Creating nested transaction with name [" + definition.getName() + "]");
			}
			if (useSavepointForNestedTransaction()) {
				// Create savepoint within existing Spring-managed transaction,
				// through the SavepointManager API implemented by TransactionStatus.
				// Usually uses JDBC 3.0 savepoints. Never activates Spring synchronization.
                //获取事务状态实例
				DefaultTransactionStatus status =
						prepareTransactionStatus(definition, transaction, false, false, debugEnabled, null);
                // 创建保存点
				status.createAndHoldSavepoint();
				return status;
			}
			else {
				// Nested transaction through nested begin and commit/rollback calls.
				// Usually only for JTA: Spring synchronization might get activated here
				// in case of a pre-existing JTA transaction.
				return startTransaction(definition, transaction, debugEnabled, null);
			}
		}

		// Assumably PROPAGATION_SUPPORTS or PROPAGATION_REQUIRED.
		if (debugEnabled) {
			logger.debug("Participating in existing transaction");
		}
		if (isValidateExistingTransaction()) {
			if (definition.getIsolationLevel() != TransactionDefinition.ISOLATION_DEFAULT) {
				Integer currentIsolationLevel = TransactionSynchronizationManager.getCurrentTransactionIsolationLevel();
				if (currentIsolationLevel == null || currentIsolationLevel != definition.getIsolationLevel()) {
					Constants isoConstants = DefaultTransactionDefinition.constants;
					throw new IllegalTransactionStateException("Participating transaction with definition [" +
							definition + "] specifies isolation level which is incompatible with existing transaction: " +
							(currentIsolationLevel != null ?
									isoConstants.toCode(currentIsolationLevel, DefaultTransactionDefinition.PREFIX_ISOLATION) :
									"(unknown)"));
				}
			}
			if (!definition.isReadOnly()) {
				if (TransactionSynchronizationManager.isCurrentTransactionReadOnly()) {
					throw new IllegalTransactionStateException("Participating transaction with definition [" +
							definition + "] is not marked as read-only but existing transaction is");
				}
			}
		}
		boolean newSynchronization = (getTransactionSynchronization() != SYNCHRONIZATION_NEVER);
		return prepareTransactionStatus(definition, transaction, false, newSynchronization, debugEnabled, null);
	}

        总结一下上面的内容就是,如果内部事务传播类型为 PROPAGATION_NEVER, 则会直接报错,因为它不允许有外部事务,如果是 PROPAGATION_NOT_SUPPORTED 就会挂起当前事务,直接返回事务的状态。PROPAGATION_REQUIRES_NEW 则会在挂起当前事务之后再开启一个新的事务。PROPAGATION_NESTED 则会设置一个保存点以与外部事务区分边界。此外还可以注意到出了 PROPAGATION_REQUIRES_NEW 返回的事务状态中设置 newTransaction 状态为true, 其他几类返回的要么 false(代表融合到外部事务中),要么就直接报错了。suspend 方法的返回值是数据库连接持有器,即这个方法把当前事务的数据库连接拿出来了,还在缓存中清除了对应的缓存。在 PROPAGATION_REQUIRES_NEW 类型的内部事务发生异常的时候,又会把该数据库连接重新放入缓存中。

        总结一下获取事务状态的 getTransaction 方法,外部事务和内部事务调用它获取事务状态的逻辑是不同的。不过目的也都是获取事务对象实例和数据库连接。

提交事务 commit        

       public final void commit(TransactionStatus status) throws TransactionException {
        // 如果已经提交过,则直接报错
		if (status.isCompleted()) {
			throw new IllegalTransactionStateException(
					"Transaction is already completed - do not call commit or rollback more than once per transaction");
		}

		DefaultTransactionStatus defStatus = (DefaultTransactionStatus) status;
        //如果事务在处理过程中被设置了回滚标志,在这里就会回滚
		if (defStatus.isLocalRollbackOnly()) {
			if (defStatus.isDebug()) {
				logger.debug("Transactional code has requested rollback");
			}
			processRollback(defStatus, false);
			return;
		}

		if (!shouldCommitOnGlobalRollbackOnly() && defStatus.isGlobalRollbackOnly()) {
			if (defStatus.isDebug()) {
				logger.debug("Global transaction is marked as rollback-only but transactional code requested commit");
			}
			processRollback(defStatus, true);
			return;
		}
        //提交逻辑
		processCommit(defStatus);
	}


    private void processCommit(DefaultTransactionStatus status) throws TransactionException {
		try {
			boolean beforeCompletionInvoked = false;

			try {
				boolean unexpectedRollback = false;
				prepareForCommit(status);
				triggerBeforeCommit(status);
				triggerBeforeCompletion(status);
				beforeCompletionInvoked = true;
                //如果有保存点的话,需要将保存点释放掉
				if (status.hasSavepoint()) {
					if (status.isDebug()) {
						logger.debug("Releasing transaction savepoint");
					}
					unexpectedRollback = status.isGlobalRollbackOnly();
					status.releaseHeldSavepoint();
				}
				else if (status.isNewTransaction()) {
					if (status.isDebug()) {
						logger.debug("Initiating transaction commit");
					}
					unexpectedRollback = status.isGlobalRollbackOnly();
                    //执行提交任务的方法
					doCommit(status);
				}
				else if (isFailEarlyOnGlobalRollbackOnly()) {
					unexpectedRollback = status.isGlobalRollbackOnly();
				}

				// Throw UnexpectedRollbackException if we have a global rollback-only
				// marker but still didn't get a corresponding exception from commit.
				if (unexpectedRollback) {
					throw new UnexpectedRollbackException(
							"Transaction silently rolled back because it has been marked as rollback-only");
				}
			}
			catch (UnexpectedRollbackException ex) {
				// can only be caused by doCommit
				triggerAfterCompletion(status, TransactionSynchronization.STATUS_ROLLED_BACK);
				throw ex;
			}
			catch (TransactionException ex) {
				// can only be caused by doCommit
				if (isRollbackOnCommitFailure()) {
					doRollbackOnCommitException(status, ex);
				}
				else {
					triggerAfterCompletion(status, TransactionSynchronization.STATUS_UNKNOWN);
				}
				throw ex;
			}
			catch (RuntimeException | Error ex) {
				if (!beforeCompletionInvoked) {
					triggerBeforeCompletion(status);
				}
				doRollbackOnCommitException(status, ex);
				throw ex;
			}

			// Trigger afterCommit callbacks, with an exception thrown there
			// propagated to callers but the transaction still considered as committed.
			try {
				triggerAfterCommit(status);
			}
			finally {
				triggerAfterCompletion(status, TransactionSynchronization.STATUS_COMMITTED);
			}

		}
		finally {
			cleanupAfterCompletion(status);
		}
	}

    protected void doCommit(DefaultTransactionStatus status) {
		DataSourceTransactionObject txObject = (DataSourceTransactionObject) status.getTransaction();
        //获取数据库连接
		Connection con = txObject.getConnectionHolder().getConnection();
		if (status.isDebug()) {
			logger.debug("Committing JDBC transaction on Connection [" + con + "]");
		}
		try {
            // 也是调用数据库连接的 commit 方法提交
			con.commit();
		}
		catch (SQLException ex) {
			throw new TransactionSystemException("Could not commit JDBC transaction", ex);
		}
	}

        commit 方法有很多同步处理器的逻辑,但是核心方法也还是获取到数据库连接,然后进行提交。此外,如果有保存点的话还要将保存点清除掉。

回滚事务 rollback

        

    public final void rollback(TransactionStatus status) throws TransactionException {
		if (status.isCompleted()) {
			throw new IllegalTransactionStateException(
					"Transaction is already completed - do not call commit or rollback more than once per transaction");
		}

		DefaultTransactionStatus defStatus = (DefaultTransactionStatus) status;
		processRollback(defStatus, false);
	}

    private void processRollback(DefaultTransactionStatus status, boolean unexpected) {
		try {
			boolean unexpectedRollback = unexpected;

			try {
				triggerBeforeCompletion(status);
                // 如果事务有保存点的话,回滚只会回滚到保存点
				if (status.hasSavepoint()) {
					if (status.isDebug()) {
						logger.debug("Rolling back transaction to savepoint");
					}
					status.rollbackToHeldSavepoint();
				}
				else if (status.isNewTransaction()) {
					if (status.isDebug()) {
						logger.debug("Initiating transaction rollback");
					}
                    // 新事务的回滚逻辑
					doRollback(status);
				}
				else {
					// Participating in larger transaction
					if (status.hasTransaction()) {
						if (status.isLocalRollbackOnly() || isGlobalRollbackOnParticipationFailure()) {
							if (status.isDebug()) {
								logger.debug("Participating transaction failed - marking existing transaction as rollback-only");
							}
							doSetRollbackOnly(status);
						}
						else {
							if (status.isDebug()) {
								logger.debug("Participating transaction failed - letting transaction originator decide on rollback");
							}
						}
					}
					else {
						logger.debug("Should roll back transaction but cannot - no transaction available");
					}
					// Unexpected rollback only matters here if we're asked to fail early
					if (!isFailEarlyOnGlobalRollbackOnly()) {
						unexpectedRollback = false;
					}
				}
			}
			catch (RuntimeException | Error ex) {
				triggerAfterCompletion(status, TransactionSynchronization.STATUS_UNKNOWN);
				throw ex;
			}

			triggerAfterCompletion(status, TransactionSynchronization.STATUS_ROLLED_BACK);

			// Raise UnexpectedRollbackException if we had a global rollback-only marker
			if (unexpectedRollback) {
				throw new UnexpectedRollbackException(
						"Transaction rolled back because it has been marked as rollback-only");
			}
		}
		finally {
			cleanupAfterCompletion(status);
		}
	}

    protected void doRollback(DefaultTransactionStatus status) {
		DataSourceTransactionObject txObject = (DataSourceTransactionObject) status.getTransaction();
        //获取数据库连接
		Connection con = txObject.getConnectionHolder().getConnection();
		if (status.isDebug()) {
			logger.debug("Rolling back JDBC transaction on Connection [" + con + "]");
		}
		try {
            //通过数据库连接进行回滚
			con.rollback();
		}
		catch (SQLException ex) {
			throw new TransactionSystemException("Could not roll back JDBC transaction", ex);
		}
	}

          rolllback 方法跟 commit 方法很像,如果有保存点的话就回滚到保存点,如果没有的话就会直接进行回滚。且都是依赖数据库连接进行操作。

总结

        本文介绍了事务管理器的功能,比如事务的创建,数据库连接的获取,不同事务传播类型所对应的事务状态,事务的提交和回滚等。可是本文的内容都是一个个点,我们知道了事务管理器可以获取到事务的状态,可以根据事务状态进行提交回滚,可是什么时候需要创建一个事务呢,获取事务状态信息时需要传入事务定义的参数,它是什么时候解析的呢?怎么根据事务状态来协调多个事务的执行呢?事务和我们真正的逻辑方法怎么绑定起来的呢,为什么方法出错事务就会回滚呢?事务什么时候才能成功提交呢?下一篇文章将会继续探索这些问题,看看Spring 到底怎么跟事务管理器整合来实现对业务方法进行事务管理的。

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值