首页 > 数据库 >【Mybatis】【SQL执行过程】【三】Mybatis源码解析-SqlSession、Executor的创建

【Mybatis】【SQL执行过程】【三】Mybatis源码解析-SqlSession、Executor的创建

时间:2023-03-08 21:12:40浏览次数:35  
标签:执行器 事务 transaction executor SqlSession 源码 Executor Mybatis configuration

1  前言

上节我们看到 MapperMethod 执行的前奏,看到其实都是调用的 SqlSession 去执行的,而 SqlSession 又是调用其内部的 Executor 来进行执行的,那么这节我们先来看下回顾这两者的创建过程,方便下节讲执行哈。

2  SqlSession 的创建

起源是通过我们的 SqlSessionFactory 来进行创建的我们来看下:

// DefaultSqlSessionFactory 默认不自动提交
public SqlSession openSession() {
  return openSessionFromDataSource(configuration.getDefaultExecutorType(), null, false);
}
// DefaultSqlSessionFactory 
// autoCommit 是否开启自动提交
public SqlSession openSession(boolean autoCommit) {
  return openSessionFromDataSource(configuration.getDefaultExecutorType(), null, autoCommit);
}

我们看到是内部调用 openSessionFromDataSource 来完成创建的,我们跟进去看下:

private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) {
  Transaction tx = null;
  try {
    // 环境变量
    final Environment environment = configuration.getEnvironment();
    // 事务工厂  JdbcTransactionFactory(自动帮你) ManagedTransactionFactory(自己管理事务)
    final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment);
    // 根据我们的事务工厂获取一个事务对象 每个事务对象里边都有数据源  连接也是从其数据源中获取的
    tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);
    // 创建我们的执行器
    final Executor executor = configuration.newExecutor(tx, execType);
    // 创建 SqlSession 对象
    return new DefaultSqlSession(configuration, executor, autoCommit);
  } catch (Exception e) {
    closeTransaction(tx); // may have fetched a connection so lets call close()
    throw ExceptionFactory.wrapException("Error opening session.  Cause: " + e, e);
  } finally {
    ErrorContext.instance().reset();
  }

可以看到创建 SqlSession 的大致步骤:

  1. 从事务工厂获取一个事务连接
  2. 创建执行器
  3. 创建SqlSession

那我们分别来看下这三者的具体执行。

2.1  获取连接

关于事务的我们之前说过了哈,我们这里简单回顾下,就是根据我们的环境配置的事务管理器信息以及数据源信息,根据 type 值去创建对应的事务工厂,然后从事务工厂获取连接,事务工厂其实也是从数据源中获取,我画个图简单回顾下哈:

2.2  执行器的创建

那我们再继续看下执行器的创建,执行器就是负责执行我们的语句的,执行器的类型大致也分三种,如果开启了二级缓存的话会把执行器外边再包一层缓存对象,我们来具体看下:

// 默认的执行器类型 simple 的
protected ExecutorType defaultExecutorType = ExecutorType.SIMPLE;
public Executor newExecutor(Transaction transaction, ExecutorType executorType) {
  // 最终获取一个执行器的类型
  executorType = executorType == null ? defaultExecutorType : executorType;
  executorType = executorType == null ? ExecutorType.SIMPLE : executorType;
  Executor executor;
  /**
   * 根据类型创建不同的执行器
   * 每个的实例化都很简单都是直接调用父类的实例化 BaseExecutor
   */
  if (ExecutorType.BATCH == executorType) {
    executor = new BatchExecutor(this, transaction);
  } else if (ExecutorType.REUSE == executorType) {
    executor = new ReuseExecutor(this, transaction);
  } else {
    executor = new SimpleExecutor(this, transaction);
  }
  // 这个就是控制二级缓存的  开启的话会把我们的执行器外边包一层缓存对象 加入缓存的逻辑
  if (cacheEnabled) {
    executor = new CachingExecutor(executor);
  }
  // 插件
  executor = (Executor) interceptorChain.pluginAll(executor);
  return executor;
}

 下边三个执行器的实例化方法都会调用 BaseExecutor 的实例化,我们看下:

// SimpleExecutor 实例化
public SimpleExecutor(Configuration configuration, Transaction transaction) {
  super(configuration, transaction);
}
// BatchExecutor 实例化
public BatchExecutor(Configuration configuration, Transaction transaction) {
  super(configuration, transaction);
}
// ReuseExecutor 实例化
public ReuseExecutor(Configuration configuration, Transaction transaction) {
  super(configuration, transaction);
}

那我们进入 BaseExecutor,看一下实例化都做了哪些操作:

// BaseExecutor
protected BaseExecutor(Configuration configuration, Transaction transaction) {
  // 事务对象也是连接对象,里边有连接
  this.transaction = transaction;
  // 延迟加载相关的
  this.deferredLoads = new ConcurrentLinkedQueue<>();
  // 一级缓存
  this.localCache = new PerpetualCache("LocalCache");
  this.localOutputParameterCache = new PerpetualCache("LocalOutputParameterCache");
  this.closed = false;
  this.configuration = configuration;
  this.wrapper = this;
}

可以看到执行器的实例化会初始化一级缓存、连接等信息。

2.3  SqlSession的实例化

那么对于 SqlSession 的实例化就比较简单了,我们来看下:

public DefaultSqlSession(Configuration configuration, Executor executor, boolean autoCommit) {
  // 配置
  this.configuration = configuration;
  // 核心大哥执行器 我们增删改查都是通过这里的执行器进行的
  this.executor = executor;
  // 是否脏了
  this.dirty = false;
  // 自动提交标志
  this.autoCommit = autoCommit;
}

默认的创建的是 DefaultSqlSession 以及创建的执行器、配置等信息。

3  小困惑

我们的自动提交最后是给了谁了,谁给我们把事务自动提交的呢,我在调试的时候有这个困惑,发现我的事务管理器也是 JDBC 类型的,但是事务就是没提交,是怎么回事呢?

并且我的数据源是 Druid 的,获取的连接默认是开启事务的:

那么当我获取连接的时候:

// 获取连接
public Connection getConnection() throws SQLException {
  if (connection == null) {
    openConnection();
  }
  return connection;
}
protected void openConnection() throws SQLException {
  if (log.isDebugEnabled()) {
    log.debug("Opening JDBC Connection");
  }
  connection = dataSource.getConnection();
  if (level != null) {
    connection.setTransactionIsolation(level.getLevel());
  }
  // 这里会判断自动提交的意愿,就是你要不要自动提交呢
  setDesiredAutoCommit(autoCommit);
}
protected void setDesiredAutoCommit(boolean desiredAutoCommit) {
  try {
    /**
     * connection.getAutoCommit() 是 true,默认是开启自动提交的
     * desiredAutoCommit 我们openSession的时候没有传自动开启提交,那么就是 false 所以两者不一样
     */
    if (connection.getAutoCommit() != desiredAutoCommit) {
      if (log.isDebugEnabled()) {
        log.debug("Setting autocommit to " + desiredAutoCommit + " on JDBC Connection [" + connection + "]");
      }
      // 就会把连接的自动提交关闭为 false 所以事务不会自动提交了
      connection.setAutoCommit(desiredAutoCommit);
    }
  } catch (SQLException e) {
    // Only a very poorly implemented driver would fail here,
    // and there's not much we can do about that.
    throw new TransactionException("Error configuring AutoCommit.  "
        + "Your driver may not support getAutoCommit() or setAutoCommit(). "
        + "Requested setting: " + desiredAutoCommit + ".  Cause: " + e, e);
  }
}

不知道你有没有这个困惑哈,总之我们的自动提交最终是设置给数据库连接的,默认的连接时开启自动事务的,当我们openSession没有开启自动提交的话,就会将连接中的自动提交关闭,所以事务不会自动提交了。

4  小结

这节我们简单回顾了下 SqlSession 以及内部的 Executor的创建,默认的 SqlSession 的类型是 DefaultSqlSession ,执行器的话默认的创建的是 SimpleExecutor 类型的,当开启了二级缓存的话(默认是开启的),我们的执行器外边会包装成 CachingExecutor,那么下节我们就具体看一下执行过程。

标签:执行器,事务,transaction,executor,SqlSession,源码,Executor,Mybatis,configuration
From: https://www.cnblogs.com/kukuxjx/p/17195924.html

相关文章

  • mybatis01_mybatis入门
    一、MyBatis简介​ MyBatis本是apache的一个开源项目iBatis,2010年这个项目由apachesoftwarefoundation迁移到了googlecode,并且改名为MyBatis。2013年11月迁移到Github......
  • mybatis02_Mapper代理开发
    1、创建项目并添加依赖、连接数据库,编写mybatis的配置文件项目结构如下所需依赖如下(创建的是聚合工程,请根据自己的是实际情况选择合适的版本)<properties><ma......
  • Mybatis 源码分析
    转自:https://juejin.cn/post/6983853041686577189mybatis是当今Java项目使用最为广泛的ORM框架,免除了几乎所有的JDBC代码以及设置参数和获取结果集的工作。本文将会带大家......
  • vuex-router-sync 源码解析
    vuex-router-sync:路由状态管理,保持vue-router和vuex存储同步。import{sync}from'vuex-router-sync'importrouterfrom'@/router'importstorefrom'@/store'syn......
  • mybatis种的ResultMap嵌套
    mybatis中的返回类嵌套一个list,如何实现?<resultMapid="CusMap"type="com.yang.webstarter.entity.SysUser"><collectionproperty="books"javaType="java......
  • Tomcat 中的 NIO 源码分析
    转自:https://javadoop.com/post/tomcat-nio之前写了两篇关于NIO的文章,第一篇介绍了NIO的Channel、Buffer、Selector使用,第二篇介绍了非阻塞IO和异步IO,并展示了简......
  • java springboot mybatis plus 3.4 实现执行任意 sql 语句
    试了SqlRunner一直失败,不知道原因,于是试了如下方法,完美解决。@AutowiredprivateSqlSessionFactorysqlSessionFactory;publicList<Map<String,Object>>exec......
  • 直播网站源码,vue工具类,时间格式化
    直播网站源码,vue工具类,时间格式化最近做uniapp经常用到时间格式化,需要转为刚刚、几分钟前等字符串格式,自己根据需求整理了一个工具类 效果说明:时间转字符串格式,时间戳......
  • Mybatis 实现多条件、多值批量更新
    UPDATEcourseSETname=CASEidWHEN1THEN'name1'WHEN2THEN'name2'WHEN3THEN'name3'END,title=CASEid......
  • python 多线程, 多进程, ProcessPoolExecutor. 有关GIL锁的问题实验.
    importtimeimportthreadingfrommultiprocessingimportProcessfromconcurrent.futuresimportProcessPoolExecutordeftime_decorator(func):defwrapper......