Mybatis(三) - Mybatis是如何通过SqlSessionFactory得到SqlSession的

Mybatis是如何通过SqlSessionFactory得到SqlSession的

本篇文章是通过看视频学习总结的内容, 如有错误的地方请谅解,并联系博主及时修改,谢谢您的阅读.

源码地址: mybatis 中文注释版

《Mybatis(二) - Mybatis是如何创建出SqlSessionFactory的》
《Mybatis(一) - Mybatis 最原始是使用方式》

《Mybatis(一) - Mybatis 最原始是使用方式》 中 Junit 测试方法开始,通过 SqlSession session = this.sqlSessionFactory.openSession(); SqlSessionFactory得到 SqlSessoin,那么在本文中将通过源码的角度来鉴赏Mybatis为查询创建会话。

  • 进入方法 sqlSessionFactory.openSession() 中,会发现SqlSession居然只是个接口,Ctrl + alt + B 发现有两个实现类,一个是 DefaultSqlSessionFactory,另一个是SessionManager ,很明显,在《Mybatis(二)》文章结尾处,Mybatis 此时只是用了 DefaultSqlSessionFactory,还没有涉及到其他的类,那么我们只需要跟进 DefaultSqlSessionFactory 即可,从代码中可以看出5步就完成了 session 的创建,那么再细细看一下5步具体干了什么。
private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) {
    Transaction tx = null;
    try {
      // 1  得到配置的数据源信息
      final Environment environment = configuration.getEnvironment();
      // 2   获取事务工厂
      final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment);
      // 3 创建事务
      tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);
      // 4 根据事务工厂和默认的执行器类型,创建执行器 >>
      final Executor executor = configuration.newExecutor(tx, execType);
      // 5 创建默认的sqlSession,创建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();
    }
  }
一、configuration.getEnvironment();
  • 1.1 这一步很简单,只是从初始化完毕后的 Configuration 对象中获取环境,提供给第二步创建事务的时候使用,我都忘了环境变量是怎么读取的了,我得去看看,找到org.apache.ibatis.builder.xml.XMLConfigBuilder#parseConfiguration() 方法,原来是数据源啊。
private void parseConfiguration(XNode root) {
    try {
      ....
      // 数据源的创建
      environmentsElement(root.evalNode("environments"));
      .....
    } catch (Exception e) {
    }
  }
  • 1.2 实在想不起这玩意儿是干啥的,就继续进入 mybatis-config.xml 查看 environments 节点
<environments default="development">
   <environment id="development">
       <transactionManager type="JDBC"/><!-- 单独使用时配置成MANAGED没有事务 -->
       <dataSource type="POOLED">
           <property name="driver" value="${jdbc.driver}"/>
           <property name="url" value="${jdbc.url}"/>
           <property name="username" value="${jdbc.username}"/>
           <property name="password" value="${jdbc.password}"/>
       </dataSource>
   </environment>
</environments>
二、getTransactionFactoryFromEnvironment(environment);
  • 2.1 通过用户设置的数据源信息创建事务工厂,这里事务工厂先放一放,这里不做详细分析
private TransactionFactory getTransactionFactoryFromEnvironment(Environment environment) {
    if (environment == null || environment.getTransactionFactory() == null) {
      return new ManagedTransactionFactory();
    }
    return environment.getTransactionFactory();
  }
三、transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit)

在聊这个方法的前提,是先搞明白方法三个参数的意义

environment.getDataSource(): dataSource数据源,配置在 mybatis-config.xml中 environments 节点中
level:  追溯到该方法的调用处,是一个null值,通过字面意思可以理解为创建会话的等级
autoCommit: PrepareStatment执行完毕后会有提交操作,这里的autoCommit就是设置 ps.commit(); 的
  • 3.1 TransactionFactory 是一个接口,实现类有两个(JdbcTransactionFactoryManagedTransactionFactory),在第二步中 如果数据源或者是数据源事务工厂不存在的话,那么肯定是创建的 ManagedTransactionFactory(), 但是通过前面的分析结果,其实 environment == null || environment.getTransactionFactory() == null 不满足情况,那么肯定是走 else 的逻辑,最终直接拿到了数据源中的事务工厂
四、configuration.newExecutor(tx, execType)
  • 4.1 这一步就很明显了,开始通过配置文件中的执行器类型和事务,去创建执行器
    那么来看看执行器有哪些
public enum ExecutorType {
  // SIMPLE: 简单执行器 (默认执行器),可以通过方法configuration.getDefaultExecutorType() 找到,定义在 Configuration 对象中 protected ExecutorType defaultExecutorType = ExecutorType.SIMPLE;
  // REUSE:  重用执行器
  // BATCH:  批量执行器
  SIMPLE, REUSE, BATCH
}
  • 4.2 进入 newExecutor(), 在这个方法里面,不仅创建了 SimpleExecutor 对象,如果开启二级缓存的情况下还对 Excutor 做了缓存的装饰,最后还对插件进行了植入,这里先留一个悬念,为什么植入插件会是在执行器中完成?
 /**
   * 创建sql执行器
   * @param transaction 事务
   * @param executorType  执行器类型: BATCH REUSE SIMPLE 三种,默认是SIMPLE类型
   * @return
   */
  public Executor newExecutor(Transaction transaction, ExecutorType executorType) {
    executorType = executorType == null ? defaultExecutorType : executorType;
    executorType = executorType == null ? ExecutorType.SIMPLE : executorType;
    Executor executor;
    if (ExecutorType.BATCH == executorType) {
      executor = new BatchExecutor(this, transaction);
    } else if (ExecutorType.REUSE == executorType) {
      executor = new ReuseExecutor(this, transaction);
    }
    // 默认会走这里的逻辑
    else {
      // 默认 SimpleExecutor
      executor = new SimpleExecutor(this, transaction);
    }
    // 二级缓存开关,settings 中的 cacheEnabled 默认是 true
    if (cacheEnabled) {
      // 这里使用了装饰器模式,来增强执行器对象
      executor = new CachingExecutor(executor);
    }
    // 植入插件的逻辑,至此,四大对象已经全部拦截完毕
    executor = (Executor) interceptorChain.pluginAll(executor);
    return executor;
  }
五、return new DefaultSqlSession(configuration, executor, autoCommit)
  • 5.1 new 出一个默认的SqlSession对象,然后返回,此时的 SqlSession 对象中已经包含了 全局配置对象、执行器、是否自动提交 这几个对象
六、第四点中遗留悬念
  • 为什么植入插件是在执行器中去完成的?

通过对 Mybatis官网 插件的描述,插件只会去拦截 Executor 、ParameterHandler、ResultSetHandler、StatementHandler 四大对象。纵观全局这四大对象都处于一个会话中的,那么毫无疑问,插件初始化肯定会存在会话创建过程中完成。但是插件又是一个全局的,存在与Configuration对象中。因此初始化执行器的时候,将插件初始化到全局Configuration

总结:

  1. 从三篇文章中,首先是初始化配置文件,得到Configuration 对象。
  2. Configuration 对象在初始化的时候注册了类的别名、数据源配置、事务工厂配置,插件的解析, sqlXML的解析。
  3. Configuration 初始化同时也完成了对 mapper.xml 文件的解析,得到了 Mapper.java + 方法名(statementId) 对应sql 的映射关系(com.example.mapper.UserMapper.getUserById --> select * from t_user where id = ?)
  4. 得到 statementId 并且绑定了 sql 执行后的返回结果类型
  5. 以上所有数据全部准备齐全后,执行调用 jdbc 的执行方法,则可以获取到返回结果,封装成 第四步得到的 pojo 类型
  • 5
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值