阅读文章能够收获 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);// 这里采用字节流构建 SqlSessionFactorySqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);// 通过 sqlSessionFactory 获取 sqlSessionSqlSession 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 参数列表
1》执行器类型 ExecutorType
ExecutorType
是一个执行器类型枚举, 里面有三种类型 SIMPLE
, BATCH
, REUSE
, 分别对应如下执行器
SimpleExecutor:
在每次执行完成后都会关闭 statement 对象, 为默认执行器
BatchExecutor:
会将修改操作记录在本地,等待程序触发或有下一次查询时才批量执行修改操作
ReuseExecutor:
会在本地维护一个容器,当前 statement 创建完成后放入容器中,当下次执行相同的 sql 时会复用 statement 对象,执行完毕后也不会关闭
2》TransactionIsolationLevel
TransactionIsolationLevel
代表了数据库的隔离级别, 也是一个枚举, 其中包含数据库的四种隔离级别
NONE:
没有隔离级别
READ_COMMITTED:
读取提交内容
READ_UNCOMMITTED:
读取未提交内容
REPEATABLE_READ:
可重复读
SERIALIZABLE:
可串行化
3》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
创建的过程
不是我不写, 而是: 写出来就是另一个故事了, [捂脸]
<br>
> 微信搜索【源码兴趣圈】,关注龙台,回复【资料】领取涵盖 GO、Netty、SpringCLoud Alibaba、Seata、开发规范、面试宝典、数据结构等电子书 or 视频学习资料!
参考资料
[1] Mybatis源码地址: https://github.com/mybatis/mybatis-3
[2] Mybatis官网地址: https://mybatis.org/mybatis-3