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

一、前言

        在上一个篇章我们主要介绍了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封装txAttrif (txAttr != null && txAttr.getName() == null) {txAttr = new DelegatingTransactionAttribute(txAttr) {@Overridepublic 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方法。

@Overridepublic 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());// 非新创建连接则写falsetxObject.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的规则,如果满足则进行回滚。

	@Overridepublic 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 transactionif (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 earlyif (!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 markerif (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*/@Overrideprotected 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*/@Overridepublic 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 transactionif (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 earlyif (!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 markerif (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*/@Overrideprotected 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.rhkb.cn/news/407302.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

定制开发AI智能名片商城小程序:重塑品牌曝光的创新推手

摘要&#xff1a;随着移动互联网技术的飞速发展&#xff0c;小程序作为一种轻量级应用形态&#xff0c;正逐步成为企业品牌传播与商业变现的重要渠道。本文将探讨在品牌定位中&#xff0c;如何将“定制开发AI智能名片商城小程序”作为品牌曝光的核心推手&#xff0c;通过强化品…

力扣 | 最长公共子序列 | 动态规划 | 最长公共子序列长度、最长公共子序列

文章目录 一、1143. 最长公共子序列二、求最长公共子序列三、变式一、1035. 不相交的线二、1312. 让字符串成为回文串的最少插入次数 一、1143. 最长公共子序列 LeetCode&#xff1a;1143. 最长公共子序列 这是一道典型的二维动态规划问题&#xff0c;甚至面试都能被面到。 这…

c++ | 模板进阶

前言 本篇博客讲解c中的模板的一些其他知识 &#x1f493; 个人主页&#xff1a;普通young man-CSDN博客 ⏩ 文章专栏&#xff1a;C_普通young man的博客-CSDN博客 ⏩ 本人giee: 普通小青年 (pu-tong-young-man) - Gitee.com 若有问题 评论区见&#x1f4dd; &#x1f389;欢…

推荐一个国内Midjourney镜像站,限时充值享5折优惠 结尾附实测图片

作为一名绘画爱好者&#xff0c;你是否曾梦想过将脑海中的画面转化为现实&#xff1f;现在&#xff0c;有了群嘉智创平台&#xff08;ai.qunzjia.cn&#xff09;&#xff0c;这一切都将成为可能。群嘉智创是国内领先的AI对话与Midjourney绘画服务平台&#xff0c;通过接入国内多…

[Meachines] [Easy] Legacy nmap 漏洞扫描脚本深度发现+MS08-067

信息收集 IP AddressOpening Ports10.10.10.4TCP:135,139,445 $ nmap -p- 10.10.10.4 --min-rate 1000 -sC -sV -Pn PORT STATE SERVICE VERSION 135/tcp open msrpc Microsoft Windows RPC 139/tcp open netbios-ssn Microsoft Windows n…

论文阅读1 Scaling Synthetic Data Creation with 1,000,000,000 Personas

Scaling Synthetic Data Creation with 1,000,000,000 Personas 链接&#xff1a;https://github.com/tencent-ailab/persona-hub/ 文章目录 Scaling Synthetic Data Creation with 1,000,000,000 Personas1. 摘要2. 背景2.1 什么是数据合成2.2 为什么需要数据合成2.3 10亿种人…

基于Kotlin Multiplatform的鸿蒙跨平台开发实践

一、 背景 在 2023 年的华为开发者大会&#xff08;HDC&#xff09;上&#xff0c;华为预告了一个全新的鸿蒙系统 Harmony Next 版本。与之前的鸿蒙系统不同&#xff0c;Harmony Next完全摒弃了对 AOSP 的兼容&#xff0c;彻底基于 OpenHarmony 开源鸿蒙实现。这意味着该系统将…

在idea中的git选择某一次记录拉出一个新分支

一 创建新分支 1.1 操作步骤 需求&#xff1a;需要在图中标红的历史记录&#xff0c;从此记录拉出一个分支 1.右键【new branch】 2.起一个新的名字&#xff1a; 3.新分支代码

《图解设计模式》笔记(四)分开考虑

九、Bridge模式&#xff1a;将类的功能层次结构与实现层次结构分离 类的两个层次结构和作用 类的功能层次结构&#xff1a;希望增加新功能时 父类有基本功能&#xff0c;在子类中增加新功能 Something父类 …├─SomethingGood子类 想要再增加新功能 Something父类 …├─So…

LeetCode.55.跳跃游戏(贪心算法思路)

题目描述&#xff1a; 给你一个非负整数数组 nums &#xff0c;你最初位于数组的 第一个下标 。数组中的每个元素代表你在该位置可以跳跃的最大长度。 判断你是否能够到达最后一个下标&#xff0c;如果可以&#xff0c;返回 true &#xff1b;否则&#xff0c;返回 false 输…

Docker容器镜像及其打包

容器镜像分类 1. 系统类镜像 2. 应⽤镜像 搜索镜像 # 默认docker.hub docker search centos 下载镜像 docker pull centos 默认下载最新版本 1. 打包 [rootdocker001 ~]# systemctl start docker.service [rootdocker001 ~]# docker save -o centos.tar centos:latest [root…

基于SpringBoot的线上教学平台系统

你好呀&#xff0c;我是计算机学姐码农小野&#xff01;如果有相关需求&#xff0c;可以私信联系我。 开发语言 Java 数据库 MySQL 技术 SpringBoot框架&#xff0c;Java语言 工具 IDEA/Eclipse、Navicat、Maven 系统展示 首页 管理员功能模块 学员功能模块 前台首页…

Ozon在奥伦堡州开设首个配送中心,Ozon还有机会赚钱吗?

Ozon平台成立于1998年&#xff0c;是俄罗斯唯一上市的B2C电商平台&#xff0c;在俄罗斯电商市场中占据着到达62%的市场份额&#xff0c;具有强大的市场影响力和吸引力。Ozon拥有数千万的活跃用户&#xff0c;覆盖了俄罗斯各个年龄段和消费层次的群体&#xff0c;而且Ozon拥有俄…

“精准学”官宣将公布中国首个语音端到端大模型

教育科技公司“精准学”宣布&#xff0c;公司已在AI语音交互技术上取得领先性的突破&#xff0c;成功训练了中国首个语音端到端大模型“心流知镜-s(V02)”&#xff0c;可直接实现语音输入-语音输出的交互&#xff0c;使其更适配辅学场景&#xff0c;使大模型达到“真人老师”级…

当《黑神话:悟空》遇上openKylin,国产力量的极致碰撞!

了解更多银河麒麟操作系统全新产品&#xff0c;请点击访问 麒麟软件产品专区&#xff1a;https://product.kylinos.cn 开发者专区&#xff1a;https://developer.kylinos.cn 文档中心&#xff1a;https://documentkylinos.cn 万众瞩目的国产3A游戏巨作《黑神话&#xff1a;悟…

【人工智能】如何在白嫖的阿里云PAI平台上跑模型?

在“交互式建模&#xff08;DSW&#xff09;”中新建实例&#xff0c;阿里云自带的示例镜像是很少的&#xff0c;所以我们只需要筛选出适合你的项目的CUDA版本就好。DSW实例可以看作是一个Linux虚拟机&#xff0c;之后我们在实例中新建另一个Python环境使用即可。 新建完实例后…

DevExpress中Blazor部分学习

DevExpress中Blazor学习 1 DevExpress版本2 学习步骤2.1 查看Dev相应的Demo2.2 创建第一个相关应用2.3 使用XPO进行相关数据操作2.4 Dev Blazor使用XPO操作 3 学习中遇到问题及解决方案3.1 打开Dev相关Demo报错 1 DevExpress版本 安装较新的DevExpress&#xff0c;我这边使用的…

基于FreeRTOS的STM32多功能手表

前言 项目背景 项目演示 使用到的硬件 项目原理图 目前版本实现的功能 设计到的freertos知识 实现思路 代码讲解 初始化GPIO引脚、配置时钟 蜂鸣器初始化以及软件定时器创建 系统默认创建的defaultTaskHandle 创建七个Task&#xff0c;代表七个功能 ShowTimeTask …

京东软件测试岗面试题(干货)含答案+文档

&#x1f345; 点击文末小卡片&#xff0c;免费获取软件测试全套资料&#xff0c;资料在手&#xff0c;涨薪更快 前面看到了一些面试题&#xff0c;总感觉会用得到&#xff0c;但是看一遍又记不住&#xff0c;所以我把面试题都整合在一起&#xff0c;都是来自各路大佬的分享&am…

自然语言处理系列三十三》 语义相似度》同义词词林》算法原理

注&#xff1a;此文章内容均节选自充电了么创始人&#xff0c;CEO兼CTO陈敬雷老师的新书《自然语言处理原理与实战》&#xff08;人工智能科学与技术丛书&#xff09;【陈敬雷编著】【清华大学出版社】 文章目录 自然语言处理系列三十三同义词词林算法原理代码实战 总结 自然语…