Spring源码解析(34)之Spring事务回滚流程

news2024/12/28 17:37:43

一、前言

        在上一个篇章我们主要介绍了Spring事务的运行流程,也带着一步步debug看了整个事务的运行流程,但是还是欠缺了Spring事务的回滚的流程。

        在上篇也主要介绍了Spring事务的传播特性,这里还是要看一下Spring事务的传播特性,因为不同的事务传播特性在回滚时会用不同的操作。

        接下来将看下在不同的事务传播特性下,Spring事务回滚是如何去操作的,下面调试代码。

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd">
    <context:property-placeholder location="classpath:dbconfig.properties"></context:property-placeholder>
    <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
        <property name="username" value="${jdbc.username}"></property>
        <property name="password" value="${jdbc.password}"></property>
        <property name="url" value="${jdbc.url}"></property>
        <property name="driverClassName" value="${jdbc.driverClassName}"></property>
    </bean>
    <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate" >
        <constructor-arg name="dataSource" ref="dataSource"></constructor-arg>
    </bean>
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource"></property>
    </bean>
    <bean id="bookService" class="com.mashibing.tx.xml.service.BookService">
        <property name="bookDao" ref="bookDao"></property>
    </bean>
    <bean id="bookDao" class="com.mashibing.tx.xml.dao.BookDao">
        <property name="jdbcTemplate" ref="jdbcTemplate"></property>
    </bean>
    <aop:config>
        <aop:pointcut id="txPoint" expression="execution(* com.mashibing.tx.xml.*.*.*(..))"/>
        <aop:advisor advice-ref="myAdvice" pointcut-ref="txPoint"></aop:advisor>
    </aop:config>
    <tx:advice id="myAdvice" transaction-manager="transactionManager">
        <tx:attributes>
            <tx:method name="checkout" propagation="REQUIRED" />
            <tx:method name="updateStock" propagation="REQUIRED" />
        </tx:attributes>
    </tx:advice>
</beans>

        业务代码以及启动类代码如下:

public class TxTest {
    public static void main(String[] args) throws SQLException {
        System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY,"d:\\code");
        ApplicationContext context = new ClassPathXmlApplicationContext("tx.xml");
        BookService bookService = context.getBean("bookService", BookService.class);
        bookService.checkout("zhangsan",1);
    }
}


public class BookService {

    BookDao bookDao;

    public BookDao getBookDao() {
        return bookDao;
    }

    public void setBookDao(BookDao bookDao) {
        this.bookDao = bookDao;
    }

    /**
     * 结账:传入哪个用户买了哪本书
     * @param username
     * @param id
     */
    public void checkout(String username,int id){

        try {
            bookDao.updateStock(id);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

public class BookDao {

    JdbcTemplate jdbcTemplate;

    public JdbcTemplate getJdbcTemplate() {
        return jdbcTemplate;
    }

    public void setJdbcTemplate(JdbcTemplate jdbcTemplate) {
        this.jdbcTemplate = jdbcTemplate;
    }

    /**
     * 减去某个用户的余额
     * @param userName
     * @param price
     */
    public void updateBalance(String userName,int price){
        String sql = "update account set balance=balance-? where username=?";
        jdbcTemplate.update(sql,price,userName);
    }

    /**
     * 按照图书的id来获取图书的价格
     * @param id
     * @return
     */
    public int getPrice(int id){
        String sql = "select price from book where id=?";
        return jdbcTemplate.queryForObject(sql,Integer.class,id);
    }

    /**
     * 减库存,减去某本书的库存
     * @param id
     */
    public void updateStock(int id){
        String sql = "update book_stock set stock=stock-1 where id=?";
        jdbcTemplate.update(sql,id);
        for (int i = 1 ;i>=0 ;i--)
            System.out.println(10/i);
    }
}

        我们外层事务跟内层事务的隔离级别都是required,然后在内层事务中制造出来一个异常,看下Spring事务是如何处理的。

二、Spring 事务回滚源码分析 

         复习一下,我们在事务解析的时候注入了以下对象:

        然后我们执行业务方法的时候,首先会从cglibInvocationInterceptor进去,然后生产拦截器链,然后通过cglibMethodInvocation去执行链接器链路,最后就会执行到TransactionInterceptor中。

        我们直接断点打到TransactionInterceptor中。

        在这里就会去创建一个事务,具体的逻辑可以去看一下上一篇我们介绍的事务源码流程,这里我们直接跳过,然后来执行创建对应的里层事务 。

        此时就到执行创建里面那层也就是执行updateStock时候需要去创建事务了,我们继续往里跟。

protected TransactionInfo createTransactionIfNecessary(@Nullable PlatformTransactionManager tm,
			@Nullable TransactionAttribute txAttr, final String joinpointIdentification) {

		// If no name specified, apply method identification as transaction name.
		// 如果没有名称指定则使用方法唯一标识,并使用DelegatingTransactionAttribute封装txAttr
		if (txAttr != null && txAttr.getName() == null) {
			txAttr = new DelegatingTransactionAttribute(txAttr) {
				@Override
				public String getName() {
					return joinpointIdentification;
				}
			};
		}

		TransactionStatus status = null;
		if (txAttr != null) {
			if (tm != null) {
				// 获取TransactionStatus事务状态信息
				status = tm.getTransaction(txAttr);
			}
			else {
				if (logger.isDebugEnabled()) {
					logger.debug("Skipping transactional joinpoint [" + joinpointIdentification +
							"] because no transaction manager has been configured");
				}
			}
		}
		// 根据指定的属性与status准备一个TransactionInfo,
		return prepareTransactionInfo(tm, txAttr, joinpointIdentification, status);
	}

        继续往下跟getTransaction方法。

@Override
	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();

		// 判断当前线程是否存在事务,判断依据为当前线程记录的连接不为空且连接中的transactionActive属性不为空
		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.
		// 如果当前线程不存在事务,但是PropagationBehavior却被声明为PROPAGATION_MANDATORY抛出异常
		if (def.getPropagationBehavior() == TransactionDefinition.PROPAGATION_MANDATORY) {
			throw new IllegalTransactionStateException(
					"No existing transaction found for transaction marked with propagation 'mandatory'");
		}
		// PROPAGATION_REQUIRED,PROPAGATION_REQUIRES_NEW,PROPAGATION_NESTED都需要新建事务
		else if (def.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRED ||
				def.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRES_NEW ||
				def.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NESTED) {
			//没有当前事务的话,REQUIRED,REQUIRES_NEW,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 {
			// 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);
		}
	}

        继续跟下doGetTransaction方法。

	protected Object doGetTransaction() {
		// 创建一个数据源事务对象
		DataSourceTransactionObject txObject = new DataSourceTransactionObject();
		// 是否允许当前事务设置保持点
		txObject.setSavepointAllowed(isNestedTransactionAllowed());
		/**
		 * TransactionSynchronizationManager 事务同步管理器对象(该类中都是局部线程变量)
		 * 用来保存当前事务的信息,我们第一次从这里去线程变量中获取 事务连接持有器对象 通过数据源为key去获取
		 * 由于第一次进来开始事务 我们的事务同步管理器中没有被存放.所以此时获取出来的conHolder为null
		 */
		ConnectionHolder conHolder =
				(ConnectionHolder) TransactionSynchronizationManager.getResource(obtainDataSource());
		// 非新创建连接则写false
		txObject.setConnectionHolder(conHolder, false);
		// 返回事务对象
		return txObject;
	}

        可以看到他这里是从spring的事务同步管理器中去获取一个链接,在上一个事务中,我们已经往Spring事务同步管理器中放入了一个连接,所以这个时候我们是能够获取得到连接的。

        然后此时我们是存在事务的,因为外层事务已经存在,所以我们就会走存在事务的方法。

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) {
				resumeAfterBeginException(transaction, suspendedResources, beginEx);
				throw beginEx;
			}
		}

		// 嵌套事务
		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);
	}

        我们这里是required就会走这个方法,此时他就会去创建一个事务状态信息,可以看到我们传传入的 newSynchronization为true,并且isNewTransaction为false,标记这不是一个新事务,我们记得这两个标记位,继续往下跟。

       

        然后在newTransactionStatus中又会修改一下newSynchronization的值。

	protected final DefaultTransactionStatus prepareTransactionStatus(
			TransactionDefinition definition, @Nullable Object transaction, boolean newTransaction,
			boolean newSynchronization, boolean debug, @Nullable Object suspendedResources) {

		DefaultTransactionStatus status = newTransactionStatus(
				definition, transaction, newTransaction, newSynchronization, debug, suspendedResources);
		prepareSynchronization(status, definition);
		return status;
	}

	protected DefaultTransactionStatus newTransactionStatus(
			TransactionDefinition definition, @Nullable Object transaction, boolean newTransaction,
			boolean newSynchronization, boolean debug, @Nullable Object suspendedResources) {

		// 是否需要新同步,只要有新同步且当前无同步激活事务
		boolean actualNewSynchronization = newSynchronization &&
				!TransactionSynchronizationManager.isSynchronizationActive();
		return new DefaultTransactionStatus(
				transaction, newTransaction, actualNewSynchronization,
				definition.isReadOnly(), debug, suspendedResources);
	}

	protected void prepareSynchronization(DefaultTransactionStatus status, TransactionDefinition definition) {
		if (status.isNewSynchronization()) {
			// 绑定事务激活
			TransactionSynchronizationManager.setActualTransactionActive(status.hasTransaction());
			// 当前事务的隔离级别
			TransactionSynchronizationManager.setCurrentTransactionIsolationLevel(
					definition.getIsolationLevel() != TransactionDefinition.ISOLATION_DEFAULT ?
							definition.getIsolationLevel() : null);
			// 是否为只读事务
			TransactionSynchronizationManager.setCurrentTransactionReadOnly(definition.isReadOnly());
			// 事务的名称
			TransactionSynchronizationManager.setCurrentTransactionName(definition.getName());
			TransactionSynchronizationManager.initSynchronization();
		}
	}

        通过以上代码其实我们可以知道 newSynchronization就是控制我们是否要去改变Spring事务同步器里面那几个线程变量里面的值,我们知道如果内层事务是require,他就会从外层事务,所以他这里是不用去修改线程变量里面的值,并且它也不会一个新事务,所以isNewTransaction也为false。

        此时我们已经创建好了一个事务,然后执行具体的业务方法之后就会抛出来一个异常,我们继续往下跟 completeTransactionAfterThrowing看下具体是如何去处理的。

protected void completeTransactionAfterThrowing(@Nullable TransactionInfo txInfo, Throwable ex) {
		if (txInfo != null && txInfo.getTransactionStatus() != null) {
			if (logger.isTraceEnabled()) {
				logger.trace("Completing transaction for [" + txInfo.getJoinpointIdentification() +
						"] after exception: " + ex);
			}
			if (txInfo.transactionAttribute != null && txInfo.transactionAttribute.rollbackOn(ex)) {
				try {
					// 进行回滚
					txInfo.getTransactionManager().rollback(txInfo.getTransactionStatus());
				}
				catch (TransactionSystemException ex2) {
					logger.error("Application exception overridden by rollback exception", ex);
					ex2.initApplicationException(ex);
					throw ex2;
				}
				catch (RuntimeException | Error ex2) {
					logger.error("Application exception overridden by rollback exception", ex);
					throw ex2;
				}
			}
			else {
				// We don't roll back on this exception.
				// Will still roll back if TransactionStatus.isRollbackOnly() is true.
				try {
					txInfo.getTransactionManager().commit(txInfo.getTransactionStatus());
				}
				catch (TransactionSystemException ex2) {
					logger.error("Application exception overridden by commit exception", ex);
					ex2.initApplicationException(ex);
					throw ex2;
				}
				catch (RuntimeException | Error ex2) {
					logger.error("Application exception overridden by commit exception", ex);
					throw ex2;
				}
			}
		}
	}


public boolean rollbackOn(Throwable ex) {
		if (logger.isTraceEnabled()) {
			logger.trace("Applying rules to determine whether transaction should rollback on " + ex);
		}

		RollbackRuleAttribute winner = null;
		int deepest = Integer.MAX_VALUE;

		// 处理设置的回滚规则
		if (this.rollbackRules != null) {
			for (RollbackRuleAttribute rule : this.rollbackRules) {
				int depth = rule.getDepth(ex);
				if (depth >= 0 && depth < deepest) {
					deepest = depth;
					winner = rule;
				}
			}
		}

		if (logger.isTraceEnabled()) {
			logger.trace("Winning rollback rule is: " + winner);
		}

		// User superclass behavior (rollback on unchecked) if no rule matches.
		if (winner == null) {
			logger.trace("No relevant rollback rule found: applying default rules");
			return super.rollbackOn(ex);
		}

		return !(winner instanceof NoRollbackRuleAttribute);
	}

        首先会判断是否判断rollbackOn的规则,如果满足则进行回滚。

	@Override
	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);
		}
	}

         此时我们这里没有保存点并且也不是一个新事务,但是我们又存在事务,所以我们继续往下跟。

        可以看到内层事务只是做一个回滚标记而已并没有做任何回滚操作。

	/**
	 * 设置会馆标记,如果既没有保存点,又不是新的事务,如果可以设置全局的回滚标记的话,就会设置。
	 * @param status the status representation of the transaction
	 */
	@Override
	protected void doSetRollbackOnly(DefaultTransactionStatus status) {
		DataSourceTransactionObject txObject = (DataSourceTransactionObject) status.getTransaction();
		if (status.isDebug()) {
			logger.debug("Setting JDBC transaction [" + txObject.getConnectionHolder().getConnection() +
					"] rollback-only");
		}
		txObject.setRollbackOnly();
	}

     大家要注意一下,我们这里此时外层是将事务捕获了,并没有往外部抛出异常信息。

        此时我们回到最外层事务中。

        可以看到此时虽然异常信息虽然已经是打印了,但是要知道此时外层已经将事务吞掉并没有抛出,此时就会去执行对应的事务提交的方法,我们继续往下跟。

	protected void commitTransactionAfterReturning(@Nullable TransactionInfo txInfo) {
		if (txInfo != null && txInfo.getTransactionStatus() != null) {
			if (logger.isTraceEnabled()) {
				logger.trace("Completing transaction for [" + txInfo.getJoinpointIdentification() + "]");
			}
			txInfo.getTransactionManager().commit(txInfo.getTransactionStatus());
		}
	}
/**
	 * 提交事务,就算没有异常,但是提交的时候也可能会回滚,因为有内层事务可能会标记回滚。所以这里先判断是否状态是否需要本地回滚,
	 * 也就是设置回滚标记为全局回滚,不会进行回滚,再判断是否需要全局回滚,就是真的执行回滚。但是这里如果是发现有全局回滚,还要进行提交,就会报异常
	 *
	 * This implementation of commit handles participating in existing
	 * transactions and programmatic rollback requests.
	 * Delegates to {@code isRollbackOnly}, {@code doCommit}
	 * and {@code rollback}.
	 * @see org.springframework.transaction.TransactionStatus#isRollbackOnly()
	 * @see #doCommit
	 * @see #rollback
	 */
	@Override
	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 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);
		}
	}
	/**
	 * 真正回滚的处理方法,也就是获取JDBC连接,然后回滚
	 * @param status the status representation of the transaction
	 */
	@Override
	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 {
			// jdbc的回滚
			con.rollback();
		}
		catch (SQLException ex) {
			throw new TransactionSystemException("Could not roll back JDBC transaction", ex);
		}
	}

        可以看到虽然外层事务已经将异常捕获掉没有抛出异常信息,但是内层事务已经标记了一个全局的事务回滚标记,所以在提交事务中,就会回滚事务。

        这样子就可以印证,Spring外层事务如果是required,内层事务也是required, 如果程序正常执行,那么内层事务不会提交,在外部事务中统一进行事务提交,如果内层事务,或者外层事务中出现异常情况,那么会在外层事务的处理中统一进行异常回滚。

        可以很简单的理解,因为内层是required,所以他就会用外层的事务,如果内层方法有异常,就会标记一个全局的事务回滚标记,并且在外层事务中统一提交。

三、Spring其他事物传播特性事物回滚流程

        3.1内层 required_new,外层required

         上面我们已经看了外内层事务是required,如果此时我们的内层事务如果改成required_new会是什么效果呢?

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd">
    <context:property-placeholder location="classpath:dbconfig.properties"></context:property-placeholder>
    <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
        <property name="username" value="${jdbc.username}"></property>
        <property name="password" value="${jdbc.password}"></property>
        <property name="url" value="${jdbc.url}"></property>
        <property name="driverClassName" value="${jdbc.driverClassName}"></property>
    </bean>
    <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate" >
        <constructor-arg name="dataSource" ref="dataSource"></constructor-arg>
    </bean>
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource"></property>
    </bean>
    <bean id="bookService" class="com.mashibing.tx.xml.service.BookService">
        <property name="bookDao" ref="bookDao"></property>
    </bean>
    <bean id="bookDao" class="com.mashibing.tx.xml.dao.BookDao">
        <property name="jdbcTemplate" ref="jdbcTemplate"></property>
    </bean>
    <aop:config>
        <aop:pointcut id="txPoint" expression="execution(* com.mashibing.tx.xml.*.*.*(..))"/>
        <aop:advisor advice-ref="myAdvice" pointcut-ref="txPoint"></aop:advisor>
    </aop:config>
    <tx:advice id="myAdvice" transaction-manager="transactionManager">
        <tx:attributes>
            <tx:method name="checkout" propagation="REQUIRED" />
            <tx:method name="updateStock" propagation="REQUIRES_NEW" />
        </tx:attributes>
    </tx:advice>
</beans>

        我们此时断点也是直接打在创建内层事务中。 

        此时可以看到内层事务状态两个标记位都是true,表示这是一个新事务,然后我们接下来看下他的事务回滚操作。

        可以看到内层事务如果存在异常了,那么他直接在内层事务中进行回滚。我们断点在跳回外层事务中。

        我们在外层事务中把事务捕获掉了,并没有抛出异常,所以外层事务是会正常的提交,我们继续往下跟。

        可以看到我们的外层事务并没有标记有全局事务回滚,所以这里的事务是会正常提交的。

        所以外层如果是required,内层是requreid_new,那么如果外层方法中存在事务,内层方法在运行的时候会挂起外层事务并开启一个新的事务,如果程序正常执行,则内层方法优先事务提交,然后外层方法再提交;如果内层方法中存在异常,内层事务会优先回滚,外层方法事务也会回滚,如果外层方法中存在异常,那么内层事务正常正常提交,而外层方法会进行回滚操作

3.2 内层NOT_SUPPORTED,外层required 

         事务传播特性如下:

    <tx:advice id="myAdvice" transaction-manager="transactionManager">
        <tx:attributes>
            <tx:method name="checkout" propagation="REQUIRED" />
            <tx:method name="updateStock" propagation="NOT_SUPPORTED" />
        </tx:attributes>
    </tx:advice>

        他在创建第二个事务的时候如下:

        此时就会以无事务的状态运行,然后此时如果出现异常,则不会回滚,代码如下:

        因为此时本身是以无事务的状态运行,所以,就算此时抛出了异常信息,那么此时只是提示并不会回滚。

        所以总结如下: 外层方法中有事务,直接挂起,内层方法没有异常情况的话直接顺利执行,
如果内层方法有异常的话,那么内层方法中已经执行的数据库操作不会触发回滚,而外层方法的事务会进行回滚操作,同样,如果外层方法中出现了异常操作,那么内部方法是不会回滚的,只有外层事务才会回滚。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2055824.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

思博伦测试每秒最大新建、并发、吞吐

详细方法查看本文资源链接 一、最大新建测试说明 1、新建测试的主要目标是测试被测设备&#xff08;DUT&#xff09;的处理器能力。在单位时间内能够建立的连接数越多&#xff0c;说明被测设备的处理器的能力越强。 2、由于在测试过程中&#xff0c;我们只关心成功的建立TCP…

流动会场:定义新一代灵活空间的全新选择—轻空间

在当今快节奏的世界里&#xff0c;活动和会议的需求正变得越来越多样化和复杂化。无论是公司年会、大型宴会、还是各类演出和会议&#xff0c;场地的选择不仅需要满足功能需求&#xff0c;更要灵活、易于部署。正是在这样的背景下&#xff0c;“流动会场”这一创新概念应运而生…

反向沙箱是什么?如何使用反向沙箱保障上网安全

反向沙箱是什么&#xff1f; 反向沙箱是深信达的一种沙箱技术&#xff0c;又称SPN&#xff08;Sandbox Proxy Network&#xff09;沙箱。主要用于解决企业在安全上网过程中的风险问题。它通过在企业内部部署一个隔离的沙盒环境&#xff0c;实现安全的互联网访问&#xff0c;从而…

注意力机制(课程笔记)

一&#xff1a; 针对的问题 解决在循环卷积网络RNN模型中存在的信息瓶颈问题。 信息瓶颈&#xff1a; 举的是机器翻译的例子。在RNN中&#xff0c;Decoder的第一个输出取决于Encoder中的上一个输出&#xff0c;然后Decoder的其余输出都取决于上一个Decoder输出&#xff08;也就…

使用Nexus3为containerd和docker配置镜像代理

1.Nexus3介绍&#xff1a; Nexus3&#xff08;Nexus Repository Manager3&#xff09;是一个用于存储、组织和管理软件组件&#xff08;如 JAR文件、npm包、Docker镜像等&#xff09;的仓库管理系统。它由Sonatype开发并维护。Nexus Repository Manger支持许多流行的包管理工具…

免费仿微信聊天工具盒子IM

盒子IM是一个仿微信实现的网页版聊天软件&#xff0c;不依赖任何第三方收费组件。后端采用springbootnetty实现&#xff0c;web端使用vue&#xff0c;移动端使用uniapp&#xff0c;支持私聊、群聊、离线消息、发送图片、文件、语音、emoji表情、视频聊天等功能。包含pc端和移动…

Redis 技术详解

一、Redis 基础 &#xff08;一&#xff09;为什么使用 Redis 速度快&#xff0c;因为数据存在内存中&#xff0c;类似于 HashMap&#xff0c;查找和操作的时间复杂度都是 O(1)。支持丰富数据类型&#xff0c;支持 string、list、set、Zset、hash 等。支持事务&#xff0c;操…

Go —— 反射

反射 反射是什么&#xff1f; 反射是运行时检查自身结构的机制反射是困惑的源泉。 反射特性与 interface 紧密相关。 接口 1. 类型 Go是静态类型语言&#xff0c;比如int、float32、[]byte&#xff0c;等等。每个变量都有一个静态类型&#xff0c;而且在编译时就确定了。…

自主智能体的未来:LangChain Agents如何实现复杂任务自动化

一、AI Agents 基础&#xff1a;ReAct 范式 在AI领域&#xff0c;智能体&#xff08;Agents&#xff09;指的是能够自主感知环境并采取行动以实现特定目标的系统。ReAct&#xff08;Reasoning and Acting&#xff09;范式是理解智能体的基础&#xff0c;它强调智能体在执行任务…

【YashanDB知识库】共享集群YAC换IP

【标题】共享集群YAC换IP 【需求分类】安装部署&#xff0c;配置变更 【关键字】安装部署&#xff0c;更换IP&#xff0c;运维&#xff0c;配置变更&#xff0c;高可用&#xff0c;YAC 【需求描述】客户需要将已经部署的YAC集群更换IP&#xff0c;从测试网段切换生产网段 【…

2024年AI艺术生成器精选榜单,抢先体验!

选择合适的AI艺术生成器对于设计项目的成功至关重要。无论是从设计线框到复杂的交互原型&#xff0c;合适的工具都能帮助顺利实现目标。本文将分享2024年最受欢迎的AI艺术生成器&#xff0c;让我们一起来看看&#xff01; 即时设计 在2024年好用的AI艺术生成器中&#xff0c;…

基于STM32开发的智能家居照明系统

目录 引言环境准备工作 硬件准备软件安装与配置系统设计 系统架构硬件连接代码实现 系统初始化光线检测与自动调节手动控制与状态指示Wi-Fi通信与远程控制应用场景 家庭智能照明办公室自动化照明常见问题及解决方案 常见问题解决方案结论 1. 引言 智能家居照明系统通过集成光…

宠物空气净化器是智商税吗吗?哪款最好用?

在当今社会&#xff0c;随着生活节奏不断加快&#xff0c;许多人会感到孤独。因此养猫已成为许多家庭的生活方式之一。他们期待着家里有欢声笑语的出现&#xff0c;希望家里一推开门都是有猫咪等着自己&#xff0c;在自己无人诉说心事的时候&#xff0c;猫咪能给自己一份陪伴。…

图神经网络教程2——循环图神经网络-2

目录 计算下游输出 序列图数据的扩展 图长短期记忆网络 循环转换在RGNN应用于图分类时的作用 数据集 算法 结果和讨论 门控循环单元 优缺点 前文索引 本篇是GRNN的第二篇文章&#xff0c;点击此处可到达第一篇文章的位置。 计算下游输出 一旦我们以图中的每个顶点为…

【PyTorch】深度学习PyTorch加载数据

系列文章目录 【PyTorch】深度学习PyTorch环境配置及安装【详细清晰】 文章目录 系列文章目录前言一、Dataset与DataloaderDatasetDataloader 二、使用步骤Dataset类的使用2.读入数据 前言 pytorch的数据加载中关于如何操作数据主要涉及Dataset和DataLoader两个类&#xff0c…

桶射巡飞无人机技术详解

无人机&#xff08;Launcher-Deployed Loitering Munition, LDLM&#xff09;作为一种新型无人机系统&#xff0c;融合了远程发射、长时续航、精确打击与多任务执行能力&#xff0c;近年来在军事侦察、目标监视、精确打击以及民用领域如环境监测、应急救援等方面展现出巨大潜力…

Elasticsearch + Search UI 构建一个文件搜索引擎

目录 Elasticsearch使用优势App Search Search UI配置engine集中管理配置和提供实用工具函数配置和初始化一个基于Elasticsearch的搜索界面应用程序Search UI 基础用法 好书推荐 Elasticsearch 使用优势 使用ElasticSearch的主要好处在于其强大的全文搜索和实时分析能力。Elas…

JVM G1垃圾回收器简介与常用配置

简介 G1 is a generational, incremental, parallel, mostly concurrent, stop-the-world, and evacuating garbage collector which monitors pause-time goals in each of the stop-the-world pauses. 在内存空间划分上&#xff0c;G1将堆分为等大的一块区域&#xff08;regi…

手摸手系列之Linux下根据自己的jdk包构建docker镜像

项目在本地导出PDF文件正常&#xff0c;部署到Linux服务器docker容器中导出就报错&#xff0c;百撕不得姐&#xff0c;经查&#xff0c;docker依赖的openjdk版本是8u112版本&#xff0c;而我本地是8u421版本&#xff0c;那就升级jdk的小版本试试。 在docker的中央仓库[点我直达…

记录jenkins的一个错误

因为workspace 的权限多了一个s 导致构建镜像出现了失败 [rootsimetra-ecs-01 .jenkins]# ls -la | grep work -rw-r----- 1 root root 46 Aug 17 11:57 org.jenkinsci.plugins.workflow.flow.FlowExecutionList.xml drwxr-x--- 6 root root 4096 Aug 12 10:06 works…