面试前看了这篇spring事务的文章,让我多要了2k的工资(下)

简介: spring事务

正文


四、提交事务


提交事务的代码要从commitTransactionAfterReturning开始说起。

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());
    }
}


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

执行事务提交之前,先判断一下事务状态是否需要提交,如果有需要回滚的则回滚,最后提交事务交给processCommit方法。


private void processCommit(DefaultTransactionStatus status) throws TransactionException {
    try {
      boolean beforeCompletionInvoked = false;
      try {
        boolean unexpectedRollback = false;
        //空实现
        prepareForCommit(status);
        //提交会话
        triggerBeforeCommit(status);
        //解绑线程和会话绑定关系,关闭会话
        triggerBeforeCompletion(status);
        beforeCompletionInvoked = true;
        //如果配置了回滚点,说明是嵌套事务
        if (status.hasSavepoint()) {
          if (status.isDebug()) {
            logger.debug("Releasing transaction savepoint");
          }
          unexpectedRollback = status.isGlobalRollbackOnly();
          //清除回滚点
          status.releaseHeldSavepoint();
        }
        //如果是新事务
        else if (status.isNewTransaction()) {
          if (status.isDebug()) {
            logger.debug("Initiating transaction commit");
          }
          unexpectedRollback = status.isGlobalRollbackOnly();
          //提交事务
          doCommit(status);
        }
        else if (isFailEarlyOnGlobalRollbackOnly()) {
          unexpectedRollback = status.isGlobalRollbackOnly();
        }
        if (unexpectedRollback) {
          throw new UnexpectedRollbackException(
              "Transaction silently rolled back because it has been marked as rollback-only");
        }
      }
      catch (UnexpectedRollbackException ex) {
        //关闭会话工厂,关闭会话,重置属性
        triggerAfterCompletion(status, TransactionSynchronization.STATUS_ROLLED_BACK);
        throw ex;
      }
      catch (TransactionException ex) {
        // can only be caused by doCommit
        if (isRollbackOnCommitFailure()) {
          doRollbackOnCommitException(status, ex);
        }
        else {
           //关闭会话工厂,关闭会话,重置属性
          triggerAfterCompletion(status, TransactionSynchronization.STATUS_UNKNOWN);
        }
        throw ex;
      }
      catch (RuntimeException | Error ex) {
        if (!beforeCompletionInvoked) {
          triggerBeforeCompletion(status);
        }
        doRollbackOnCommitException(status, ex);
        throw ex;
      }
      try {
        //空实现
        triggerAfterCommit(status);
      }
      finally {
        //关闭会话工厂,关闭会话,重置属性
        triggerAfterCompletion(status, TransactionSynchronization.STATUS_COMMITTED);
      }
    }
    finally {
      //解绑数据库连接,重置并关闭数据库连接
      //恢复挂起的资源
      cleanupAfterCompletion(status);
    }
}

我再用一张流程图梳理一下整个流程

122.png

doCommit方法代码很简单

@Override
protected void doCommit(DefaultTransactionStatus status) {
    DataSourceTransactionObject txObject = (DataSourceTransactionObject) status.getTransaction();
    Connection con = txObject.getConnectionHolder().getConnection();
    if (status.isDebug()) {
      logger.debug("Committing JDBC transaction on Connection [" + con + "]");
    }
    try {
      con.commit();
    }
    catch (SQLException ex) {
      throw new TransactionSystemException("Could not commit JDBC transaction", ex);
    }
}

就是调用数据库连接的commit方法,清空回滚点也是调用数据库连接的releaseSavepoint方法,比较简单在这里就不深入介绍了。


五、回滚事务


回滚事务入口在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 {
        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;
        }
      }
    }
}

我们重点看一下是如何判断要回滚事务的,看一下txInfo.transactionAttribute.rollbackOn方法

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

这里又引出一个问题

默认情况下,事务是否需要回滚是根据异常类型判断的,如果异常类型为RuntimeException或者Error,则需要回滚。那么问题来了,如果异常类型是Exception需要回滚不?如果不需要回滚,怎样才能让异常类型是Exception的事务回滚?


重点看一下事务回滚方法rollback

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

再进入processRollback方法


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 {
          // jta事务 
          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);
    }
  }


再用一张流程图梳理一下流程

234.png

事务还有很多有意思的代码,由于篇幅的原因暂时先介绍到这里。

相关文章
|
1月前
|
监控 Java 应用服务中间件
高级java面试---spring.factories文件的解析源码API机制
【11月更文挑战第20天】Spring Boot是一个用于快速构建基于Spring框架的应用程序的开源框架。它通过自动配置、起步依赖和内嵌服务器等特性,极大地简化了Spring应用的开发和部署过程。本文将深入探讨Spring Boot的背景历史、业务场景、功能点以及底层原理,并通过Java代码手写模拟Spring Boot的启动过程,特别是spring.factories文件的解析源码API机制。
72 2
|
20天前
|
缓存 安全 Java
Spring高手之路26——全方位掌握事务监听器
本文深入探讨了Spring事务监听器的设计与实现,包括通过TransactionSynchronization接口和@TransactionalEventListener注解实现事务监听器的方法,并通过实例详细展示了如何在事务生命周期的不同阶段执行自定义逻辑,提供了实际应用场景中的最佳实践。
40 2
Spring高手之路26——全方位掌握事务监听器
|
18天前
|
存储 缓存 Java
Spring面试必问:手写Spring IoC 循环依赖底层源码剖析
在Spring框架中,IoC(Inversion of Control,控制反转)是一个核心概念,它允许容器管理对象的生命周期和依赖关系。然而,在实际应用中,我们可能会遇到对象间的循环依赖问题。本文将深入探讨Spring如何解决IoC中的循环依赖问题,并通过手写源码的方式,让你对其底层原理有一个全新的认识。
39 2
|
21天前
|
Java 关系型数据库 数据库
京东面试:聊聊Spring事务?Spring事务的10种失效场景?加入型传播和嵌套型传播有什么区别?
45岁老架构师尼恩分享了Spring事务的核心知识点,包括事务的两种管理方式(编程式和声明式)、@Transactional注解的五大属性(transactionManager、propagation、isolation、timeout、readOnly、rollbackFor)、事务的七种传播行为、事务隔离级别及其与数据库隔离级别的关系,以及Spring事务的10种失效场景。尼恩还强调了面试中如何给出高质量答案,推荐阅读《尼恩Java面试宝典PDF》以提升面试表现。更多技术资料可在公众号【技术自由圈】获取。
|
26天前
|
JavaScript Java 关系型数据库
Spring事务失效的8种场景
本文总结了使用 @Transactional 注解时事务可能失效的几种情况,包括数据库引擎不支持事务、类未被 Spring 管理、方法非 public、自身调用、未配置事务管理器、设置为不支持事务、异常未抛出及异常类型不匹配等。针对这些情况,文章提供了相应的解决建议,帮助开发者排查和解决事务不生效的问题。
|
1月前
|
XML Java 数据库连接
Spring中的事务是如何实现的
Spring中的事务管理机制通过一系列强大的功能和灵活的配置选项,为开发者提供了高效且可靠的事务处理手段。无论是通过注解还是AOP配置,Spring都能轻松实现复杂的事务管理需求。掌握这些工具和最佳实践,能
49 3
|
4月前
|
存储 Java
【IO面试题 四】、介绍一下Java的序列化与反序列化
Java的序列化与反序列化允许对象通过实现Serializable接口转换成字节序列并存储或传输,之后可以通过ObjectInputStream和ObjectOutputStream的方法将这些字节序列恢复成对象。
|
1月前
|
存储 缓存 算法
面试官:单核 CPU 支持 Java 多线程吗?为什么?被问懵了!
本文介绍了多线程环境下的几个关键概念,包括时间片、超线程、上下文切换及其影响因素,以及线程调度的两种方式——抢占式调度和协同式调度。文章还讨论了减少上下文切换次数以提高多线程程序效率的方法,如无锁并发编程、使用CAS算法等,并提出了合理的线程数量配置策略,以平衡CPU利用率和线程切换开销。
面试官:单核 CPU 支持 Java 多线程吗?为什么?被问懵了!
|
1月前
|
存储 算法 Java
大厂面试高频:什么是自旋锁?Java 实现自旋锁的原理?
本文详解自旋锁的概念、优缺点、使用场景及Java实现。关注【mikechen的互联网架构】,10年+BAT架构经验倾囊相授。
大厂面试高频:什么是自旋锁?Java 实现自旋锁的原理?
|
1月前
|
存储 缓存 Java
大厂面试必看!Java基本数据类型和包装类的那些坑
本文介绍了Java中的基本数据类型和包装类,包括整数类型、浮点数类型、字符类型和布尔类型。详细讲解了每种类型的特性和应用场景,并探讨了包装类的引入原因、装箱与拆箱机制以及缓存机制。最后总结了面试中常见的相关考点,帮助读者更好地理解和应对面试中的问题。
58 4