一、前言
在上一个篇章我们主要介绍了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>
他在创建第二个事务的时候如下:
此时就会以无事务的状态运行,然后此时如果出现异常,则不会回滚,代码如下:
因为此时本身是以无事务的状态运行,所以,就算此时抛出了异常信息,那么此时只是提示并不会回滚。
所以总结如下: 外层方法中有事务,直接挂起,内层方法没有异常情况的话直接顺利执行,
如果内层方法有异常的话,那么内层方法中已经执行的数据库操作不会触发回滚,而外层方法的事务会进行回滚操作,同样,如果外层方法中出现了异常操作,那么内部方法是不会回滚的,只有外层事务才会回滚。