47--Spring @Transactional声明式事物(四)事物提交和回滚

1.引

前面的分析中已经成功的创建了事物,但是方法仍在方法拦截器链中。回顾一下代码片段:

// 2.处理声明式事物
if (txAttr == null || !(tm instanceof CallbackPreferringPlatformTransactionManager)) {
    // Standard transaction demarcation with getTransaction and commit/rollback calls.
    // 2.2 创建事物
    TransactionInfo txInfo = createTransactionIfNecessary(tm, txAttr, joinpointIdentification);
    System.out.println("==创建了名为:["+joinpointIdentification+"]的事物");
    Object retVal = null;
    try {
        // This is an around advice: Invoke the next interceptor in the chain.
        // This will normally result in a target object being invoked.
        // 2.3 继续调用方法拦截器链,这里一般将会调用目标类的方法,如:com.lyc.cn.v2.day09.AccountServiceImpl.save方法
        retVal = invocation.proceedWithInvocation();
    } catch (Throwable ex) {
        // target invocation exception
        // 2.4 如果目标类方法抛出异常,则在此处理,例如:事物回滚
        completeTransactionAfterThrowing(txInfo, ex);
        throw ex;
    } finally {
        // 2.5 清除上一步创建的事物信息
        cleanupTransactionInfo(txInfo);
    }
    // 2.6 调用成功完成后执行,但不是在异常被处理后执行。如果我们不创建事务,就什么也不做。
    commitTransactionAfterReturning(txInfo);
    return retVal;
}

关于拦截器链的调用、目标方法的调用等前面都有介绍。

接下来的处理会分为两种情况(假如当前一定存在事物):有异常,事物即有可能会被回滚、也有可能会被提交;无异常,事物会被提交,接下来对两种情况逐个分析。因为catch方法的代码比较靠前,先分析有异常的情况。细心的同学可能会发现,无论是否抛出异常都会调用completeTransactionAfterThrowing方法去完成事物,只是该方法被重载了,传递的参数不同而已,当然其内部处理细节肯定是不同的。

2.回滚判断
/**
 * 处理异常以完成事物,基于配置该事物可能回滚也可能提交
 * Handle a throwable, completing the transaction.
 * We may commit or roll back, depending on the configuration.
 * @param txInfo information about the current transaction
 * @param ex     throwable encountered
 */
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);
        }
        // 1.回滚
        /**
         * txInfo.transactionAttribute.rollbackOn(ex)判断回滚的条件:
         *
         * 1. 如果自定了RollbackRuleAttribute列表,如果当前异常匹配到了RollbackRuleAttribute其中的条目,则回滚
         *    例如:可以通过rollbackFor指定触发回滚的异常@Transactional(propagation = Propagation.REQUIRED,rollbackFor = Exception.class)
         *
         * 2. 否则如果异常是RuntimeException或者Error的类型,则回滚
         *
         * 3. 其他的异常是不会回滚的,这里要注意一下...
         */
        if (txInfo.transactionAttribute != null && txInfo.transactionAttribute.rollbackOn(ex)) {
            try {
                // 执行回滚
                System.out.println("==准备回滚"+txInfo.getJoinpointIdentification());
                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;
            }
        }
        // 2.如果未能满足回滚条件,则有可能会提交事物,也有可能会回滚事物
        // 注意:如果TransactionStatus.isRollbackOnly()为true,则仍然会执行回滚
        else {
            // We don't roll back on this exception.
            // Will still roll back if TransactionStatus.isRollbackOnly() is true.
            try {
                System.out.println("==准备提交"+txInfo.getJoinpointIdentification());
                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;
            }
        }
    }
}

txInfo.transactionAttribute.rollbackOn(ex)这段代码是对当前异常是否满足回滚条件的判断。Spring事物中默认只有RuntimeException和Error两种类型的被捕获之后,事物才会回滚。因为在DefaultTransactionAttribute类中有:

public boolean rollbackOn(Throwable ex) {
	return (ex instanceof RuntimeException || ex instanceof Error);
}

但是在实际应用当中,可能会有很多自定义异常,如果想要自定义异常发生时也回滚事物,怎么办呢?这时候就要用到rollbackFor属性,例如:@Transactional(propagation = Propagation.REQUIRED,rollbackFor = MyException.class),Spring会将我们自定义的异常注册到RuleBasedTransactionAttribute类的RollbackRuleAttribute属性集合中。总结如下

  1. 匹配到自定义异常,回滚。
  2. 如果异常是RuntimeException或者Error的类型,回滚。
  3. 其他异常,不回滚。

在completeTransactionAfterThrowing方法的else分支中通过commit方法对当前事物进行了提交操作,但是,commit方法并不一定会真正的提交事物,也有可能会回滚事物,这一点留在事物提交分析中讲解,接下来看事物是如何回滚的。

3.回滚
// 执行回滚前检查事物状态
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 {
            // 1.事物回滚前调用事物同步接口
            triggerBeforeCompletion(status);
            // 2.如果有保存点,则回滚到保存点
            if (status.hasSavepoint()) {
                if (status.isDebug()) {
                    logger.debug("Rolling back transaction to savepoint");
                }
                status.rollbackToHeldSavepoint();
            }
            // 3.如果当前事物是一个新的事物,则调用doRollback执行给定事物的回滚
            else if (status.isNewTransaction()) {
                if (status.isDebug()) {
                    logger.debug("Initiating transaction rollback");
                }
                doRollback(status);
            }
            else {
                // Participating in larger transaction
                // 4.如果当前事物并非独立事物,则将当前事物的rollbackOnly属性标记为true,等到事物链完成之后,一起执行回滚

                // 如果当前存在事物,但是事物的rollbackOnly属性已经被标记为true
                // 或者globalRollbackOnParticipationFailure(返回是否仅在参与事务失败后才将现有事务全局标记为回滚)为true
                if (status.hasTransaction()) {
                    if (status.isLocalRollbackOnly() || isGlobalRollbackOnParticipationFailure()) {
                        if (status.isDebug()) {
                            logger.debug("Participating transaction failed - marking existing transaction as rollback-only");
                        }
                        System.out.println("==当前事物并非独立事物,且RollbackOnly为true\n");
                        // 则将ConnectionHolder中的rollbackOnly标记为true
                        doSetRollbackOnly(status);
                    }
                    else {
                        if (status.isDebug()) {
                            logger.debug("Participating transaction failed - letting transaction originator decide on rollback");
                        }
                    }
                }
                // 5.如果当前不存在事物,则不执行任何操作
                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;
        }
        // 6.事物回滚后调用事物同步接口
        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 {
        // 7.事物完成后清理资源
        cleanupAfterCompletion(status);
    }
}

说明:第2、4点涉及到了嵌套事物回滚,留在后面章节分析。而且针对其中的嵌套事物处理,如回滚到保存点等操作,我们留在后面的章节讲解,因为涉及到的东西太多了,在一个小节里实在很难讲解。

  1. 事物完成前、完成后触发器的调用,例如:我们在上一节的业务方法里注册了TransactionSynchronizationAdapter接口,那么在这里会分别执行我们实现了接口的方法,我们可以通过该接口来做一些额外的功能扩展。
  2. 如果有保存点,则调用rollbackToHeldSavepoint回滚到保存点(后面嵌套事物章节分析)
  3. 如果当前事物是一个新的事物,则调用doRollback执行给定事物的回滚
  4. 如果当前事物并非独立事物,则将当前事物的rollbackOnly属性标记为true,等到事物链完成之后,一起执行回滚
    大概的流程就是这样了,因为我们分析的是单service下的单个业务方法调用,所以这里我们还是只分析最简单的doRollback正常回滚调用(后面嵌套事物章节分析)

不同的事物管理器对doRollback回滚有不同的实现,这里我们只看DataSourceTransactionManager的回滚方法:

@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 {
        con.rollback();
    }
    catch (SQLException ex) {
        throw new TransactionSystemException("Could not roll back JDBC transaction", ex);
    }
}

从ConnectionHolder中拿到连接并执行回滚。这里会涉及到一些数据库底层的东西了,感兴趣的同学可以自己深入研究。

到这里事物的回滚操作就完成了,但是不要忘了,这里一定要释放掉事物相关的资源。以免造成数据库连接泄露、内存泄露等错误。

private void cleanupAfterCompletion(DefaultTransactionStatus status) {
    // 1.将当前事物状态标记为已完成
    status.setCompleted();
    // 2.清除synchronization
    if (status.isNewSynchronization()) {
        TransactionSynchronizationManager.clear();
    }
    // 3.事务完成后清理资源。
    if (status.isNewTransaction()) {
        doCleanupAfterCompletion(status.getTransaction());
    }
    // 4.从嵌套事物中恢复被挂起的资源
    if (status.getSuspendedResources() != null) {
        if (status.isDebug()) {
            logger.debug("Resuming suspended transaction after completion of inner transaction");
        }
        Object transaction = (status.hasTransaction() ? status.getTransaction() : null);
        resume(transaction, (SuspendedResourcesHolder) status.getSuspendedResources());
    }
}

第1、2很简单,第4又涉及到了事物嵌套,我们只有分析一下第3步了:

protected void doCleanupAfterCompletion(Object transaction) {
    DataSourceTransactionObject txObject = (DataSourceTransactionObject) transaction;

    // Remove the connection holder from the thread, if exposed.
    // 解绑ConnectionHolder
    if (txObject.isNewConnectionHolder()) {
        TransactionSynchronizationManager.unbindResource(obtainDataSource());
    }

    // Reset connection.
    // 重置连接
    Connection con = txObject.getConnectionHolder().getConnection();
    try {
        if (txObject.isMustRestoreAutoCommit()) {
            con.setAutoCommit(true);
        }
        DataSourceUtils.resetConnectionAfterTransaction(con, txObject.getPreviousIsolationLevel());
    }
    catch (Throwable ex) {
        logger.debug("Could not reset JDBC Connection after transaction", ex);
    }

    // 释放连接
    if (txObject.isNewConnectionHolder()) {
        if (logger.isDebugEnabled()) {
            logger.debug("Releasing JDBC Connection [" + con + "] after transaction");
        }
        DataSourceUtils.releaseConnection(con, this.dataSource);
    }

    txObject.getConnectionHolder().clear();
}

等到事物资源清理完成之后,事物就彻底的结束了。

4.提交
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;

    // 如果rollbackOnly为true,则回滚
    if (defStatus.isLocalRollbackOnly()) {
        if (defStatus.isDebug()) {
            logger.debug("Transactional code has requested rollback");
        }
        processRollback(defStatus, false);
        return;
    }

    // shouldCommitOnGlobalRollbackOnly --> 返回是否对标记为仅以全局方式回滚的事务调用
    // defStatus.isGlobalRollbackOnly() --> 实现了SmartTransactionObject并且事物的rollbackOnly被标记为true
    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);
}

关于回滚和提交的判断条件注释都写的很清晰了,对于rollbackOnly这里不做太多的介绍,留在后面的章节分析。大家只要记住,事物到了这里,可能会回滚也可能会提交即可。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

闲来也无事

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值