这次我们来说说 Mybatis 的源码, 这里只说执行的流程, 内部细节太多了, 这里只能授之以渔了. 还是最近的那段代码, 我们来回顾一下.
- package mybatis;
- import mybatis.bean.StudentBean;
- import mybatis.dao.StudentMapper;
- import org.apache.ibatis.io.Resources;
- import org.apache.ibatis.session.SqlSession;
- import org.apache.ibatis.session.SqlSessionFactory;
- import org.apache.ibatis.session.SqlSessionFactoryBuilder;
- import org.junit.Before;
- import org.junit.Test;
- import java.io.IOException;
- import java.io.InputStream;
- public class Test1 {
- public SqlSession session;
- public SqlSessionFactory sqlSessionFactory;
- @Before
- public void init() throws IOException {
- String resource = "mybatis-config.xml";
- InputStream inputStream = Resources.getResourceAsStream(resource);
- sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
- session = sqlSessionFactory.openSession();
- }
- @Test
- public void studentTest() {
- StudentMapper mapper = session.getMapper(StudentMapper.class);
- StudentBean result = mapper.selectUser(1);
- System.out.println(result);
- session.commit();
- }
- }
就是拿到流文件, 也是我们主配置文件, 进行流文件解析, 传入到 build 内, 构建成一个 sqlSessionFactory, 再由 sqlSessionFactory 得到 session, 拿到 mapper, 执行 sql, 完成.
简单的流程应该是这样的, 我们来一个稍微专业一点图.
转变为我们的代码大概就是这三步, 构建 Configuration 对象, 构建我们的 sqlsessionfactory, 得到我们的 session, 得到对应的 statement(处理参数和结果)从而得到结果集.
那么我们先从我们一步步来看, 先看我们的 Configuration 对象
Configuration 解析:
我们打开 Configuration 类可以看到, 里面很多的属性设置, 包括缓存, mapper, 插件等等, 其实就是把我们的 xml 标签转化为对象了, 这里需要说明一下的是, 这个解析过程是把所有的相关的 xml 都转为 Configuration 对象了, 包括 config.xml 和 mapper.xml. 源码太多, 我就不粘贴了. 我给你看一下我转化完成的.
这里没有什么神秘的, 就是一个 xml 解析的过程, 在 XMLConfigBuilder 类的 parse 方法, 生成了完成的 Configuration 对象, 有兴趣的可以打个断点看一下.
这里要注意的就是里面很多元素是一个对应多个的, 很多属性是 map 和 set. 顺便收一下里面是由多个构造器来构建的.
MapperAnnotationBuilder 为注解方式的构造器, 其余都是为 Configuration 服务的. 也就是说在创建 Configuration 之后很多东西就已经确定了(除了 cache). 那么又是如何生成 sqlsession 的呢. 回到我们创建完 Configuration 对象那句源码上来.
点击 build 方法进去, 我们看到是 new DefaultSqlSessionFactory 然后把我们的 Configuration 对象传递进去, 也就是说有了 Configuration 对象也就生成了我们的 DefaultSqlSessionFactory 对象. DefaultSqlSessionFactory 实现了我们的 SqlSessionFactory, 我们也就得到了 SqlSessionFactory. 接下来就是我们的 sqlsession 了
sessionsql 解析:
还是老规矩, 上个图再看源码, 比较好理解.
执行过程大概是这样的.
就是什么意思呢? 由 DefaultSqlSessionFactory 生成一个执行器 Executor, 下面是由 BaseExecutor 支撑的, 里面包含一些配置和我们的一级缓存 (是不是更深入的知道为啥一级缓存生命短了), 同时也有一个类似装饰器的 CachingExecutor, 他的下面也是需要 BaseExecutor 来支撑的, 需要查询时优先查缓存, 查询不到回到我们的 BaseExecutor 来执行. 搂一眼源码去, sqlSessionFactory.openSession() 方法也就得到了我们的 sqlsession, 我们进去看一下都写了什么.
里面的 95 行 tx 主要是从我们的 configuration 中拿到一些数据源的配置, 也就是我们图中画的来支撑 BaseExecutor 的配置, 来到 96 行, 创建 Executor, 传入了数据源配置和一个执行类型, 这里简单提一嘴, 类型主要有三种: SIMPLE(简单), REUSE(可重复使用), BATCH(批量进行), 一般我们都用的 SIMPLE. 我们再进我们的 Executor 的创建方法看看, 他们都做了什么事.
先判断了执行器类型, 简单, 可重复, 批量, 613 行就是我们的缓存执行器了, 外面那个判断就是你是否配置了二级缓存, 从而是否配置我们的缓存执行器, 底层还是 Executor, 可以点击进去看看的.
就是说我们由配置文件 config.xml,mapper.xml 生成了我们的 Configuration 对象, 将 Configuration 放置在 DefaultSqlSessionFactory 内, 生成了我们的 Executor 执行器, 准备执行 SQL. 回到主题我们继续看下一部分, 最后一个 MappedStatement
MappedStatement 解析:
记住两个问题, 增删改查, 可以归类为两种, 一种是原来的数据变化 - 增删改, 另一种是原来的数据没有变化 - 查询. 我们的 mybatis 也是这样来处理的, 主要就是 query 和 update 两种大类方法.
MappedStatement 是由调用 Executor 执行器前, 由 configuration 对象来构建的. 源码在 DefaultSqlSession 的 select*** 方法内, 这里就不详细说了. 感兴趣的可以自己去了解一下. 我们主要来说执行流程.
执行过程大致是, 1. 拿到 sql;2. 拼接参数; 3. 执行 sql;4. 封装结果集. 我们来看一下具体的源码流程.
我们上面得知, 执行器的真正执行都是在 BaseExecutor 里来执行的, 我们在 DefaultSqlSession 调用的 select 方法, 最后执行了 executor.query, 只优先走我们的 CachingExecutor 执行器, 如果没有才到我们的 BaseExecutor 执行器里面来, 我们的 sql 是查询, 不需要变动数据, 那么我们把断点打在我们的 BaseExecutor 类的 query 方法上.
147 行是清理我们的一级缓存, 如果在 mapper.xml 配置了清理标签, 这里会先清理一级缓存. queryStack 表示这个执行器是否正在被使用.
152 行开始查询我们的一级缓存, 如果为空调用 156 行, 开始我们正式的查询, 里面是放入缓存占位置, 然后执行查询, 清理缓存的占位重新放置缓存, 这里说的都是一级缓存了.
总结一下就是:
1. 拿到流文件 config.xml 和 mapper.xml;
2. 用我们的两个或多个流文件创建一个 Configuration 对象,(单一职责原则来构建的, 很多个构造器来构建的, 例如 XMLConfigBuilder)
3. 将 Configuration 对象塞给 SqlSessionFactoryBuilder 类的 build 方法, 构建 SqlSessionFactory 对象.
4.sqlSessionFactory.openSession()拿到我们的 session 对象.
5. 由 session 对象和 Configuration 对象封装参数和结果集映射, 产生对应的执行器来, 二级缓存执行器和 BaseExecutor 执行器.
6. 由二级缓存执行器 CachingExecutor 来优先查询二级缓存是否存在, 不存在执行 BaseExecutor 执行器的 query 或 update 方法.
7. 拿到结果集转换 ResultMap,session 关闭, 写入二级缓存, 返回结束.
来源: https://www.cnblogs.com/cxiaocai/p/11534137.html