Spring Trasnaction管理(3)- 事务嵌套

问题导读

  • Spring 如何管理嵌套的事务
  • Spring事务传播机制 Nested 和 RequireNew 有何区别

事务传播机制

事务的传播机制应该都比较熟悉
在日常开发中会遇到需要事务嵌套的情况
Spring在进行事务管理进入新的事务传播机制时,如果检查到当前线程已经存在线程会从getTransaction方法中调用
AbstractPlatformTransactionManager.handleExistingTransaction

private TransactionStatus handleExistingTransaction(
			TransactionDefinition definition, Object transaction, boolean debugEnabled)
			throws TransactionException {
                //如果新事务的传播机制是Never就抛出异常
		if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NEVER) {
			throw new IllegalTransactionStateException(
					"Existing transaction found for transaction marked with propagation 'never'");
		}
                //如果新事务的传播机制是NotSupported就挂起当前线程
		if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NOT_SUPPORTED) {
			if (debugEnabled) {
				logger.debug("Suspending current transaction");
			}
                        //挂起事务
			Object suspendedResources = suspend(transaction);
			boolean newSynchronization = (getTransactionSynchronization() == SYNCHRONIZATION_ALWAYS);
                        //prepareTransactionStatus最后的一个参数是挂起的信息,用于结束后恢复
			return prepareTransactionStatus(
					definition, null, false, newSynchronization, debugEnabled, suspendedResources);
		}
                //如果新事务的传播机制是RequireNew就挂起当前线程,并另起事务
		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 {
				boolean newSynchronization = (getTransactionSynchronization() != SYNCHRONIZATION_NEVER);
				DefaultTransactionStatus status = newTransactionStatus(
						definition, transaction, true, newSynchronization, debugEnabled, suspendedResources);
                                //开始新事物
				doBegin(transaction, definition);
				prepareSynchronization(status, definition);
				return status;
			}
			catch (RuntimeException beginEx) {
				resumeAfterBeginException(transaction, suspendedResources, beginEx);
				throw beginEx;
			}
			catch (Error beginErr) {
				resumeAfterBeginException(transaction, suspendedResources, beginErr);
				throw beginErr;
			}
		}
                //如果新事务的传播机制是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() + "]");
			}
                        //判断是否支持Savepoint
			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);
                                //创建savepoint
				status.createAndHoldSavepoint();
				return status;
			}
			else {
				//如果不支持(一般为JTA事务),行为类似RequireNew
				// 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.
				boolean newSynchronization = (getTransactionSynchronization() != SYNCHRONIZATION_NEVER);
				DefaultTransactionStatus status = newTransactionStatus(
						definition, transaction, true, newSynchronization, debugEnabled, null);
				doBegin(transaction, definition);
				prepareSynchronization(status, definition);
				return status;
			}
		}

		// 其他情况( PROPAGATION_SUPPORTS / 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);
	}

这个逻辑是定义在AbstractPlatformTransactionManager,是默认TransactionManager的父类,具体的事务挂起、开始等操作都是子类的方法来实现的

事务的挂起

事务是如何挂起的呢,还是拿DataSourceTransactionManager来看

        //Suspend这里主要是将TransactionSynchronizationManager内部记录的Transaction信息注销,之后handleExistingTransaction会根据传播机制来控制是否新建连接、事务
	protected final SuspendedResourcesHolder suspend(Object transaction) throws TransactionException {
		...
		Object suspendedResources = null;
		if (transaction != null) {
			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);
		...
	}

	protected Object doSuspend(Object transaction) {
		DataSourceTransactionObject txObject = (DataSourceTransactionObject) transaction;
		txObject.setConnectionHolder(null);
                //将数据库连接从TransactionSynchronizationManager中注销
		ConnectionHolder conHolder = (ConnectionHolder)
				TransactionSynchronizationManager.unbindResource(this.dataSource);
		return conHolder;
	}
        //在恢复时,会将suspendedResources中的连接重新注册到TransactionSynchronizationManager
	protected void doResume(Object transaction, Object suspendedResources) {
		ConnectionHolder conHolder = (ConnectionHolder) suspendedResources;
		TransactionSynchronizationManager.bindResource(this.dataSource, conHolder);
	}

小结

在handleExistingTransaction中,Spring规定了事务传播机制的行为,
其中RequiredNew是挂起当前连接,重新创建连接,这样来保证事务之间不会互相影响,但是在使用时要注意如果这两个事务操作的是同一条记录的话,可能会导致死锁的情况,因为他们是两个session。
而Nested是利用了JDBC3.0的savepoint来实现的,这样能很好的利用同一个连接来完成操作,避免可能发生的死锁。

原文地址:https://www.cnblogs.com/resentment/p/5753013.html