作者简介:大家好,我是smart哥,前中兴通讯、美团架构师,现某互联网公司CTO
联系qq:184480602,加我进群,大家一起学习,一起进步,一起对抗互联网寒冬
学习必须往深处挖,挖的越深,基础越扎实!
阶段1、深入多线程
阶段2、深入多线程设计模式
阶段3、深入juc源码解析
码哥源码部分
码哥讲源码-原理源码篇【2024年最新大厂关于线程池使用的场景题】
码哥讲源码-原理源码篇【揭秘join方法的唤醒本质上决定于jvm的底层析构函数】
码哥源码-原理源码篇【Doug Lea为什么要将成员变量赋值给局部变量后再操作?】
码哥讲源码【谁再说Spring不支持多线程事务,你给我抽他!】
打脸系列【020-3小时讲解MESI协议和volatile之间的关系,那些将x86下的验证结果当作最终结果的水货们请闭嘴】
核心流程-数据读取阶段
一、核心流程-数据读取阶段
- Mybatis的核心流程三大阶段是:初始化–>动态代理–>数据读写阶段,本文主要分析数据读取阶段。在完成了第一阶段的配置初始化和第二阶段的动态代理之后,我们获得了一个代理对象来面向接口编程,便捷的访问数据库,按照前一篇文章 21-Mybatis 核心流程02-代理阶段的分析我们知道底层还是会走到sqlsession,数据读取阶段就是通过SqlSession完成SQL解析,参数的映射,SQL的执行,结果的解析映射的这个过程。
二、Executor
2.1 功能
- Executor是数据读取阶段的关键,从21-Mybatis 核心流程02-代理阶段我们知道,所有的操作最后会走到sqlsession,sqlsession会调用Executor组件来执行数据库操作。比如查询操作会调用Executor的的query方法来执行,更新操作会调用Executor的update方法。从这个角度来说,开发者面对的是Sqlsession接口,而SqlSession内部是通过Executor来做数据库操作的。
2.2 实现
- Executor和子类使用了模板模式,有三个不同的子类对应三种不同的执行类型。具体可以参考:14-Mybatis源码和设计模式-5(Executor组件与模板模式,装饰器模式),了解Executor模块的设计。
public enum ExecutorType {
SIMPLE, REUSE, BATCH
}
类型 | 描述 |
---|---|
simple | 默认;使用PreparedStatement访问数据库,每次访问都创建新的PreparedStatement对象 |
reuse | 使用预编译的PreparedStatement访问数据库,会重用Statement对象 |
batch | 批量执行 |
三、执行流程分析
- 在21-Mybatis 核心流程02-代理阶段 的第五点执行流程分析中我们调试代码到了sqlSession.selectList方法,实际上是 DefaultSqlSession#selectList()方法,我们从这里开始断点调试。
3.1 DefaultSqlSession#selectList
- DefaultSqlSession#selectList内部调用Executor组件的query方法
3.2 CachingExecutor#query
- CachingExecutor采用了装饰器模式实现二级缓存,它实现了Executor接口的全部方法且在内部包装了真正的Execuor实例(BatchExecutor、ReuseExecutor或者SimpleExecutor)。在query方法可以看到在执行真正的查询操作之前会访问二级缓存,如果命中就直接返回了,因此二级缓存优先级比一级缓存高。一级缓存实在Executor的query里面实现,而二级是一个包装了Executor的类来做的,它会在调用Executor的query之前去尝试获取缓存数据。
3.3 Executor#query
- 在3.2中如果二级缓存没有命中,则会调用真正的Executor实例执行query方法,首先会进入BaseExecutor的query方法,因为BaseExecutor是其他子类的抽象父类,在BaseExecutor的query中会尝试读取一级缓存,如果缓存没有命中则会调用queryFromDatabase访问数据库,queryFromDatabase方法是由不同的子类自行实现的,这是模板模式的体现。
3.4 BaseExecutor#queryFromDatabase
- queryFromDatabase中会访问一级缓存并做相关的细节处理,比如查询数据库成功之后回写一级缓存等,这里会去调用抽象方法doQuery,走到子类的实现逻辑。
3.5 SimpleExecutor#doQuery
- SimpleExecutor#doQuery是走数据库查询的入口,前面的2次缓存都没有命中就会执行数据库的查询操作。后面就引出了三大对象了,这里就不详细跟进了,在第四节给出简单的流程,详细可参考三大对象的分析逻辑,链接在参考文章的[1][2][3]。
四、读取阶段数据流
- 方法栈
--> SqlSession.selectList
--> CachingExecutor#query(二级缓存)
--> Executor#query
--> BaseExecutor#query(一级缓存)
--> BaseExecutor#queryFromDatabase(访问数据库)
--> SimpleExecutor#doQuery
--> StatementHandler获取Statement
--> StatementHandler.parameterize处理参数(内部调用DefaultParameterHandler#setParameters)
--> StatementHandler.query()
--> ResultSetHandler.handleResultSets处理结果集(时机调用DefaultResultSetHandler.handleResultSets)
- 这个流程本文只分析到SimpleExecutor#doQuery ,后面的流程实际上是由另外三大对象完成的,StatemntHandler执行数据库访问,ParameterHandler处理参数,ResultTypeHandler处理结果集,详细请阅读参考文章的[1][2][3]。