Mybatis SqlSession 如果面试官问如何创建?

apei_02
发布于 2022-4-16 18:44
浏览
0收藏

阅读文章能够收获 SqlSessionFactoryBuilder, SqlSessionFactory、SqlSession 相关知识

“建议结合 mybatis 源码[1]、mybatis 官网[2] 阅读, 可以下载不同版本的源码, 本文根据 3.4.x 解读, 不同版本源码存在细微差别
为防止不必要的麻烦, 直接下载 3.4.x 版本的 mybatis 源码, 文章中测试代码是在 org.apache.ibatis.autoconstructor 目录下建立新的文件夹进行测试

01、相关概念简介
1.1 SqlSession
负责执行 select、insert、update、delete 等命令, 同时负责获取映射器和管理事务; 其底层封装了与 JDBC 的交互, 可以说是 mybatis 最核心的接口之一

SqlSession 非线程安全, 所以每个线程都拥有一个实例; 通俗来说来一个请求就打开一个对应的 SqlSession, 使用后进行关闭

在我们日常项目中基本都是使用 mybatis, spring 整合版, 生命周期和线程安全问题不需要考虑

“SqlSession 简单理解的话, 就是你写的 SQL, 想要请求数据库, 都得经过它调取

1.2 SqlSessionFactory
负责创建 SqlSession 的工厂, 一旦被创建就应该在应用运行期间一直存在, 不需要额外再进行创建

平常项目中也是配置为 spring 中一个单例 bean

1.3 SqlSessionFactoryBuilder
主要是负责创建 SqlSessionFactory 的构造器类, 其中使用到了构建者设计模式; 创建 SqlSessionFactory 后就没啥子用了

在 spring 与 mybatis 的整合项目中, 构建 SqlSessionFactory 的任务交给了 SqlSessionFactoryBean, 在这里了解即可, 核心思想和处理结果是一致的

02、SqlSession 创建代码
下载源码之后, 在源码项目的 test目录 下面创建自己的测试包, 然后从文章底部获取 mybatis官网链接 复制创建 SqlSession 的代码, 粘贴运行即可

@Test
public void buildSqlSession() throws IOException {
    // mybatis-config.xml 文件路径
    String resource = "org/apache/ibatis/autoconstructor/mybatis-config.xml";
    // 根据 mybatis io 资源包下的工具类直接获取流
    InputStream inputStream = Resources.getResourceAsStream(resource);
    // 这里采用字节流构建 SqlSessionFactory
    SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
    // 通过 sqlSessionFactory 获取 sqlSession
    SqlSession sqlSession = sqlSessionFactory.openSession();
    System.out.println(sqlSession);
}

下面会从 SqlSessionFactoryBuilder 、 SqlSessionFactory 、 SqlSession 按照创建关系进行解析

03、解析 SqlSessionFactoryBuilder
通过源码看出, build() 大部分重载方法基本围绕 Reader、InputStream 这两个对象, 真正构建会话工厂的方法为 build(Configuration config)

public SqlSessionFactory build(Reader reader)
public SqlSessionFactory build(Reader reader, String environment)
public SqlSessionFactory build(Reader reader, Properties properties)
public SqlSessionFactory build(Reader reader, String environment, Properties properties)
public SqlSessionFactory build(InputStream inputStream)
public SqlSessionFactory build(InputStream inputStream, String environment)
public SqlSessionFactory build(InputStream inputStream, Properties properties)
public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties)
// 创建 SqlSessionFactory 的调用方法
public SqlSessionFactory build(Configuration config)

上面入参为 Reader 的 build() 也是通过 InputStream 创建的, 从字节流转换成了字符流

@Test
public void buildSelSessionByReader() throws IOException {
    final Reader reader = Resources.getResourceAsReader("org/apache/ibatis/autoconstructor/mybatis-config.xml");
    SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(reader);
}

字节流转换字符流方法

public static Reader getResourceAsReader(String resource) throws IOException {
    Reader reader;
    if (charset == null) {
        reader = new InputStreamReader(getResourceAsStream(resource));
    } else {
        reader = new InputStreamReader(getResourceAsStream(resource), charset);
    }
    return reader;
}

SqlSessionFactoryBuilder 大部分 build() 方法都是为了解析出 Configuration 对象

“具体的 Configuration 解析构建比较复杂, 会单独写篇文章说明

解析出Configuration 对象后开始创建 SqlSessionFactory, 可以看下具体实现

public SqlSessionFactory build(Configuration config) {
    return new DefaultSqlSessionFactory(config);
}

04、解析 SqlSessionFactory
SqlSessionFactory 是一个接口并非具体的类, 其具体实现类是在 build(Configuration config) 方法中创建的 new DefaultSqlSessionFactory(config)

在实现类的构造方法中并无实际逻辑, 只是把 config 对象赋值给了内部变量 Configuration

public DefaultSqlSessionFactory(Configuration configuration) {
    this.configuration = configuration;
}

05、解析 SqlSession
通过 04 章节已经可以得到创建 SqlSession 的 SqlSessionFactory 对象了, 那么接下来就是要解析本篇的重头戏 SqlSession

SqlSessionFactory 接口中声明了一系列 opensession 方法,用来返回 SqlSession 对象

SqlSession openSession();
SqlSession openSession(boolean autoCommit);
SqlSession openSession(Connection connection);
SqlSession openSession(TransactionIsolationLevel level);
SqlSession openSession(ExecutorType execType);
SqlSession openSession(ExecutorType execType, boolean autoCommit);
SqlSession openSession(ExecutorType execType, TransactionIsolationLevel level);
SqlSession openSession(ExecutorType execType, Connection connection);

“Mybatis 框架作者 Clinton Begin 可能不太喜欢写注释, 框架里面大部分接口和类都没有发现注释

在实现类 DefaultSqlSessionFactory 中实现了接口中定义的方法, 如下

public SqlSession openSession() {
    return openSessionFromDataSource(configuration.getDefaultExecutorType(), null, false);
}

说一下返回的 openSessionFromDataSource 方法, 重载的方法其实大致都一致, 这里挑典型说明

private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) {
    Transaction tx = null;
    try {
        final Environment environment = configuration.getEnvironment();
        final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment);
        tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);
        final Executor executor = configuration.newExecutor(tx, execType);
        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();
    }
}

5.1 参数列表
执行器类型 ExecutorType
ExecutorType 是一个执行器类型枚举, 里面有三种类型 SIMPLE, BATCH, REUSE, 分别对应如下执行器

SimpleExecutor: 在每次执行完成后都会关闭 statement 对象, 为默认执行器

BatchExecutor: 会将修改操作记录在本地,等待程序触发或有下一次查询时才批量执行修改操作

ReuseExecutor: 会在本地维护一个容器,当前 statement 创建完成后放入容器中,当下次执行相同的 sql 时会复用 statement 对象,执行完毕后也不会关闭

TransactionIsolationLevel
TransactionIsolationLevel 代表了数据库的隔离级别, 也是一个枚举, 其中包含数据库的四种隔离级别

NONE: 没有隔离级别

READ_COMMITTED: 读取提交内容

READ_UNCOMMITTED: 读取未提交内容

REPEATABLE_READ: 可重复读

SERIALIZABLE: 可串行化

autoCommit
第三个参数为是否自动提交事务, 默认不自动提交

5.2 方法代码解析
读取 MyBatis-Config.xml 形成 Environment 对象

final Environment environment = configuration.getEnvironment();

根据 Environment 当作入参, 获取事务工厂

final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment);

事务工厂根据数据源, 数据库隔离级别, 是否自动提交事务标识创建事务对象

Transactiontx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);

根据事务对象和不同的执行器类型创建不同的 Mybatis Executor, 通过这里可以得知, 事务和执行器是绑定的

final Executor executor = configuration.newExecutor(tx, execType);

SqlSession 也是一个接口, DefaultSqlSession 为默认实现类

根据全局配置对象, 执行器, 是否自动提交事务标识创建 DefaultSqlSession 对象

return new DefaultSqlSession(configuration, executor, autoCommit)

06、学习总结
在写这篇文章的时候, 构思的思路本来是挺清晰的, 但是越写感觉知识面越多, 所以只能把构建 SqlSession 主流程写出来

如果留心或者阅读过 mybatis 源码的同学应该能看出以下几项

  • XMLConfigBuilder 没有说明构建过程
  • Configuration 的对象构建略过
  • Transaction 创建的过程

不是我不写, 而是: 写出来就是另一个故事了, [捂脸]

整体而言, 自我感觉还是挺不错的, 哈哈哈哈

参考资料
[1]Mybatis源码地址: https://github.com/mybatis/mybatis-3

[2]Mybatis官网地址: https://mybatis.org/mybatis-3

本文转载自微信公众号「龙台的技术笔记」

分类
收藏
回复
举报
回复
    相关推荐