1.引
上一节简单介绍了Spring中的嵌套事物,接下来针对每一种传播特性,单独分析,当然前提是已经存在一个事物
2.PROPAGATION_NEVER
该特性规定了以非事物方式执行,如果当前存在事物,则抛出异常。
// 1.PROPAGATION_NEVER --> 以非事物方式执行,如果当前存在事物,则抛出异常。 if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NEVER) { throw new IllegalTransactionStateException("Existing transaction found for transaction marked with propagation 'never'"); }
3.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); // 创建新事物,注意:transaction参数为null,所以这里创建的不是一个真正的事物 return prepareTransactionStatus( definition, null, false, newSynchronization, debugEnabled, suspendedResources); }
这里又涉及到一个概念,事物挂起(这个前面没有介绍,因为前面都是单个事物)。下面分析一下事物挂起的流程:
protected final SuspendedResourcesHolder suspend(@Nullable Object transaction) throws TransactionException { // 1.如果存在事物同步回调接口 if (TransactionSynchronizationManager.isSynchronizationActive()) { // 1.1 挂起事务同步回调接口 List<TransactionSynchronization> suspendedSynchronizations = doSuspendSynchronization(); try { // 挂起事物 Object suspendedResources = null; if (transaction != null) { suspendedResources = doSuspend(transaction); } // 获取已有事物名称 String name = TransactionSynchronizationManager.getCurrentTransactionName(); // 清空已有事物名称 TransactionSynchronizationManager.setCurrentTransactionName(null); // 获取已有事物的readOnly属性值 boolean readOnly = TransactionSynchronizationManager.isCurrentTransactionReadOnly(); // 将已有事物的readOnly属性值设置为false TransactionSynchronizationManager.setCurrentTransactionReadOnly(false); // 获取已有事物数据库事物隔离级别 Integer isolationLevel = TransactionSynchronizationManager.getCurrentTransactionIsolationLevel(); // 清空已有事物数据库事物隔离级别 TransactionSynchronizationManager.setCurrentTransactionIsolationLevel(null); // 获取已有事物激活标识 boolean wasActive = TransactionSynchronizationManager.isActualTransactionActive(); // 将当前事物激活标识设置为false TransactionSynchronizationManager.setActualTransactionActive(false); // 返回SuspendedResourcesHolder /** * 将上面获取到的一系列事物属性,重新封装至SuspendedResourcesHolder对象,并返回 */ return new SuspendedResourcesHolder(suspendedResources, suspendedSynchronizations, name, readOnly, isolationLevel, wasActive); } catch (RuntimeException | Error ex) { // doSuspend failed - original transaction is still active... doResumeSynchronization(suspendedSynchronizations); throw ex; } } // 不存在事物同步回调接口,且当前事物不为空 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; } }
首先来看事物同步回调接口,该接口会在prepareSynchronization方法中设置,即使我们没有自定义事物同步回调接口,Spring默认也会为当前事物创建一个空的事物同步回调接口。关于该接口的使用,可以参考上一节中的例子。(注意:事物同步回调接口是与当前线程绑定的)。下面看事物同步接口是如何挂起的:
private List<TransactionSynchronization> doSuspendSynchronization() { // 1.获取当前线程的所有事物同步回调 List<TransactionSynchronization> suspendedSynchronizations = TransactionSynchronizationManager.getSynchronizations(); // 2.循环并挂起所有同步回调接口 for (TransactionSynchronization synchronization : suspendedSynchronizations) { synchronization.suspend(); } // 3.清除资源 TransactionSynchronizationManager.clearSynchronization(); return suspendedSynchronizations; }
其次来看挂起事物的流程,注释里已经写了很清楚了,最后会将已有的事物属性封装到SuspendedResourcesHolder对象中,该类就持有了被挂起事物的属性。接下来调用prepareTransactionStatus方法并将suspendedResources入参,这样一来,新创建的事物就持有了被挂起事物的的属性,就会形成一个事物链。而且新创建的事物transaction参数为null,所以PROPAGATION_NOT_SUPPORTED特性是不会真正开启事物的。
return prepareTransactionStatus( definition, null, false, newSynchronization, debugEnabled, suspendedResources);
4.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 { 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 beginEx) { resumeAfterBeginException(transaction, suspendedResources, beginEx); throw beginEx; } }
该特性下会挂起已有事物并新建一个事物。
5.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() + "]"); } /** * 下面对JtaTransactionManager和AbstractPlatformTransactionManager分别进行处理 */ // useSavepointForNestedTransaction(),是否为嵌套事务使用保存点 // 1.对于JtaTransactionManager-->返回false // 2.对于AbstractPlatformTransactionManager-->返回true 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. // 创建保存点在现有spring管理事务,通过TransactionStatus SavepointManager API实现。 // 通常使用JDBC 3.0保存点。永远不要激活Spring同步。 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. // 通过嵌套的开始,提交调用,及回滚调用进行嵌套事务。 // 只对JTA有效,如果已经存在JTA事务,这里可能会激活Spring同步。 boolean newSynchronization = (getTransactionSynchronization() != SYNCHRONIZATION_NEVER); DefaultTransactionStatus status = newTransactionStatus( definition, transaction, true, newSynchronization, debugEnabled, null); doBegin(transaction, definition); prepareSynchronization(status, definition); return status; } }
这里对AbstractPlatformTransactionManager和JtaTransactionManager分别做了不同的处理,重点看前者。AbstractPlatformTransactionManager是允许使用保存点的。接下来看保存点的创建过程:
// 创建保存点 public void createAndHoldSavepoint() throws TransactionException { setSavepoint(getSavepointManager().createSavepoint()); }
// 获取SavepointManager protected SavepointManager getSavepointManager() { Object transaction = this.transaction; if (!(transaction instanceof SavepointManager)) { throw new NestedTransactionNotSupportedException( "Transaction object [" + this.transaction + "] does not support savepoints"); } // SavepointManager就是当前事物 return (SavepointManager) transaction; }
// 创建保存点 public Object createSavepoint() throws TransactionException { // 获取ConnectionHolder ConnectionHolder conHolder = getConnectionHolderForSavepoint(); try { // 如果当前连接不支持保存点,抛出异常 if (!conHolder.supportsSavepoints()) { throw new NestedTransactionNotSupportedException( "Cannot create a nested transaction because savepoints are not supported by your JDBC driver"); } // 如果当前连接的RollbackOnly已经被标记为true,抛出异常 if (conHolder.isRollbackOnly()) { throw new CannotCreateTransactionException( "Cannot create savepoint for transaction which is already marked as rollback-only"); } // 创建保存点 return conHolder.createSavepoint(); } catch (SQLException ex) { throw new CannotCreateTransactionException("Could not create JDBC savepoint", ex); } }
总体来讲创建保存点的过程比较简单,最终会调用数据库驱动的底层方法去创建保存点,感兴趣的可以自己跟踪查看。创建完保存点之后将结果设置到TransactionStatus对象中。
该特性下会将新事物在原有事物内,以嵌套的方式运行,该特性也不会开启一个新的事物
6. PROPAGATION_SUPPORTS和PROPAGATION_REQUIRED
假如事物传播特性是PROPAGATION_SUPPORTS或PROPAGATION_REQUIRED的话,将会进行如下处理:
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);
isValidateExistingTransaction()
该函数的作用是指定新事物参与已有事物时,新旧两个事物的验证级别。该属性值默认为false,宽松范围的验证,也就是不验证。如果将该属性值改为true的话,那么将会验证新旧两个事物的数据库事物隔离级别、事物只读属性是否相同。对于PROPAGATION_MANDATORY,从Spring的注释上没有看到对改属性的处理,但是该属性也会走这个分支,即使用已有事物,由于到这里肯定是已经存在事物的,该特性在这里不会抛出异常。
该特性也不会开启一个新的事物,而是继续在原有事物中运行或者以非事物方式运行