精尽MyBatis源码分析 - MyBatis 的 SQL 执行过程(一)之 Executor
该系列文档是本人在学习 Mybatis 的源码过程中总结下来的,可能对读者不太友好,请结合我的源码注释(Mybatis源码分析 GitHub 地址、Mybatis-Spring 源码分析 GitHub 地址、Spring-Boot-Starter 源码分析 GitHub 地址)进行阅读
MyBatis 版本:3.5.2
MyBatis-Spring 版本:2.0.3
MyBatis-Spring-Boot-Starter 版本:2.1.4
MyBatis的SQL执行过程
在前面一系列的文档中,我已经分析了MyBatis的基础支持层以及整个的初始化过程,此时MyBatis已经处于就绪状态了,等待使用者发号施令了
那么接下来我们来看看它执行SQL的过程,该过程比较复杂,因为涉及到二级缓存,将返回结果转换成Java对象和延迟加载等等处理过程,这里将一步一步地进行分析
MyBatis中SQL执行的整体过程如下图所示:
在SqlSession中,会将执行SQL的过程交由Executor
执行器去执行,过程大致如下:
- 通过
DefaultSqlSessionFactory
创建一个与数据的SqlSession
会话,其中会创建一个Executor
执行器对象 - 然后
Executor
执行器通过StatementHandler
创建对应的java.sql.Statement
对象,并通过ParameterHandler
设置参数,然后操作数据库 - 如果是更新数据的操作,则可能需要通过
KeyGenerator
设置自增键,返回受影响的行数 - 如果是查询操作,则将操作数据库返回的
ResultSet
结果集对象包装成ResultSetWrapper
,然后通过DefaultResultSetHandler
对结果集进行映射,返回Java对象
上面还涉及到一级缓存、二级缓存和延迟加载等其他处理过程
SQL执行过程(一)之Executor
在MyBatis的SQL执行过程中,Executor执行器担当着一个重要的角色,相关操作都需要通过它来执行,相当于一个调度器,把SQL语句交给它,它来调用各个组件执行操作
其中一级缓存和二级缓存都是在Executor执行器中完成的
Executor
执行器接口的实现类如下图所示:
org.apache.ibatis.executor.BaseExecutor
:实现Executor接口,提供骨架方法,支持一级缓存,指定几个抽象的方法交由不同的子类去实现org.apache.ibatis.executor.SimpleExecutor
:继承 BaseExecutor 抽象类,简单的 Executor 实现类(默认)org.apache.ibatis.executor.ReuseExecutor
:继承 BaseExecutor 抽象类,可重用的 Executor 实现类,相比SimpleExecutor,在Statement执行完操作后不会立即关闭,而是缓存起来,执行的SQL作为key,下次执行相同的SQL时优先从缓存中获取Statement对象org.apache.ibatis.executor.BatchExecutor
:继承 BaseExecutor 抽象类,支持批量执行的 Executor 实现类org.apache.ibatis.executor.CachingExecutor
:实现 Executor 接口,支持二级缓存的 Executor 的实现类,实际采用了装饰器模式,装饰对象为左边三个Executor类
Executor
org.apache.ibatis.executor.Executor
:执行器接口,代码如下:
public interface Executor {
/**
* ResultHandler 空对象
*/
ResultHandler NO_RESULT_HANDLER = null;
/**
* 更新或者插入或者删除
* 由传入的 MappedStatement 的 SQL 所决定
*/
int update(MappedStatement ms, Object parameter) throws SQLException;
/**
* 查询,带 ResultHandler + CacheKey + BoundSql
*/
<E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler,
CacheKey cacheKey, BoundSql boundSql) throws SQLException;
/**
* 查询,带 ResultHandler
*/
<E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler)
throws SQLException;
/**
* 查询,返回 Cursor 游标
*/
<E> Cursor<E> queryCursor(MappedStatement ms, Object parameter, RowBounds rowBounds) throws SQLException;
/**
* 刷入批处理语句
*/
List<BatchResult> flushStatements() throws SQLException;
/**
* 提交事务
*/
void commit(boolean required) throws SQLException;
/**
* 回滚事务
*/
void rollback(boolean required) throws SQLException;
/**
* 创建 CacheKey 对象
*/
CacheKey createCacheKey(MappedStatement ms, Object parameterObject, RowBounds rowBounds, BoundSql boundSql);
/**
* 判断是否缓存
*/
boolean isCached(MappedStatement ms, CacheKey key);
/**
* 清除本地缓存
*/
void clearLocalCache();
/**
* 延迟加载
*/
void deferLoad(MappedStatement ms, MetaObject resultObject, String property, CacheKey key, Class<?> targetType);
/**
* 获得事务
*/
Transaction getTransaction();
/**
* 关闭事务
*/
void close(boolean forceRollback);
/**
* 判断事务是否关闭
*/
boolean isClosed();
/**
* 设置包装的 Executor 对象
*/
void setExecutorWrapper(Executor executor);
}
执行器接口定义了操作数据库的相关方法:
- 数据库的读和写操作
- 事务相关
- 缓存相关
- 设置延迟加载
- 设置包装的 Executor 对象
BaseExecutor
org.apache.ibatis.executor.BaseExecutor
:实现Executor接口,提供骨架方法,指定几个抽象的方法交由不同的子类去实现,例如:
protected abstract int doUpdate(MappedStatement ms, Object parameter) throws SQLException;
protected abstract List<BatchResult> doFlushStatements(boolean isRollback) throws SQLException;
protected abstract <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds,
ResultHandler resultHandler, BoundSql boundSql) throws SQLException;
protected abstract <E> Cursor<E> doQueryCursor(MappedStatement ms, Object parameter, RowBounds rowBounds,
BoundSql boundSql) throws SQLException;
上面这四个方法交由不同的子类去实现,分别是:更新数据库、刷入批处理语句、查询数据库和查询数据返回游标
构造方法
public abstract class BaseExecutor implements Executor {
private static final Log log = LogFactory.getLog(BaseExecutor.class);
/**
* 事务对象
*/
protected Transaction transaction;
/**
* 包装的 Executor 对象
*/
protected Executor wrapper;
/**
* DeferredLoad(延迟加载)队列
*/
protected ConcurrentLinkedQueue<DeferredLoad> deferredLoads;
/**
* 本地缓存,即一级缓存,内部就是一个 HashMap 对象
*/
protected PerpetualCache localCache;
/**
* 本地输出类型参数的缓存,和存储过程有关
*/
protected PerpetualCache localOutputParameterCache;
/**
* 全局配置
*/
protected Configuration configuration;
/**
* 记录当前会话正在查询的数量
*/
protected int queryStack;
/**
* 是否关闭
*/
private boolean closed;
protected BaseExecutor(Configuration configuration, Transaction transaction) {
this.transaction = transaction;
this.deferredLoads = new ConcurrentLinkedQueue<>();
this.localCache = new PerpetualCache("LocalCache");
this.localOutputParameterCache = new PerpetualCache("LocalOutputParameterCache");
this.closed = false;
this.configuration = configuration;
this.wrapper = this;
}
}
其中上面的属性可根据注释进行查看
这里提一下localCache
属性,本地缓存,用于一级缓存,MyBatis的一级缓存是什么呢?
每当我们使用 MyBatis 开启一次和数据库的会话,MyBatis 都会创建出一个 SqlSession 对象,表示与数据库的一次会话,而每个 SqlSession 都会创建一个 Executor 对象
在对数据库的一次会话中,我们有可能会反复地执行完全相同的查询语句,每一次查询都会访问一次数据库,如果在极短的时间内做了完全相同的查询,那么它们的结果极有可能完全相同,由于查询一次数据库的代价很大,如果不采取一些措施的话,可能造成很大的资源浪费
为了解决这一问题,减少资源的浪费,MyBatis 会在每一次 SqlSession 会话对象中建立一个简单的缓存,将每次查询到的结果缓存起来,当下次查询的时候,如果之前已有完全一样的查询,则会先尝试从这个简单的缓存中获取结果返回给用户,不需要再进行一次数据库查询了
精尽MyBatis源码分析 - MyBatis 的 SQL 执行过程(一)之 Executor的更多相关文章
- 精尽MyBatis源码分析 - MyBatis初始化(四)之 SQL 初始化(下)
该系列文档是本人在学习 Mybatis 的源码过程中总结下来的,可能对读者不太友好,请结合我的源码注释(Mybatis源码分析 GitHub 地址.Mybatis-Spring 源码分析 GitHub ...
- 精尽 MyBatis 源码分析 - MyBatis 初始化(一)之加载 mybatis-config.xml
该系列文档是本人在学习 Mybatis 的源码过程中总结下来的,可能对读者不太友好,请结合我的源码注释(Mybatis源码分析 GitHub 地址.Mybatis-Spring 源码分析 GitHub ...
- 精尽MyBatis源码分析 - MyBatis初始化(二)之加载Mapper接口与XML映射文件
该系列文档是本人在学习 Mybatis 的源码过程中总结下来的,可能对读者不太友好,请结合我的源码注释(Mybatis源码分析 GitHub 地址.Mybatis-Spring 源码分析 GitHub ...
- 精尽 MyBatis 源码分析 - MyBatis 初始化(三)之 SQL 初始化(上)
该系列文档是本人在学习 Mybatis 的源码过程中总结下来的,可能对读者不太友好,请结合我的源码注释(Mybatis源码分析 GitHub 地址.Mybatis-Spring 源码分析 GitHub ...
- MyBatis源码分析-MyBatis初始化流程
MyBatis 是支持定制化 SQL.存储过程以及高级映射的优秀的持久层框架.MyBatis 避免了几乎所有的 JDBC 代码和手动设置参数以及获取结果集.MyBatis 可以对配置和原生Map使用简 ...
- springboot集成mybatis源码分析-mybatis的mapper执行查询时的流程(三)
例: package com.example.demo.service; import com.example.demo.dao.UserDao; import com.example.demo.do ...
- 精尽MyBatis源码分析 - 文章导读
该系列文档是本人在学习 Mybatis 的源码过程中总结下来的,可能对读者不太友好,请结合我的源码注释(Mybatis源码分析 GitHub 地址.Mybatis-Spring 源码分析 GitHub ...
- MyBatis源码分析(各组件关系+底层原理
MyBatis源码分析MyBatis流程图 下面将结合代码具体分析. MyBatis具体代码分析 SqlSessionFactoryBuilder根据XML文件流,或者Configuration类实例 ...
- 精尽 MyBatis 源码分析 - 整体架构
该系列文档是本人在学习 Mybatis 的源码过程中总结下来的,可能对读者不太友好,请结合我的源码注释(Mybatis源码分析 GitHub 地址.Mybatis-Spring 源码分析 GitHub ...
随机推荐
- matplotlib作图 归零编码、曼切斯特编码、非归零编码、差分曼切斯特编码
效果图 代码 import matplotlib.pyplot as plt config = { 'color': 'black', 'lw': 5, } def init(): plt.figur ...
- linux修改环境变量后无法登录
在登陆界面按Ctrl+Alt+F1(F1~F6), 进入 tty 后登陆账号. 执行以下命令: /usr/bin/sudo /usr/bin/vi /etc/environment 将PATH的值复原 ...
- JS里的小细节,持续更新
判断把值定为 false 集合 JavaScript里把 null.undefined.0.''.NaN 都视为false,而其他值一概为 true Map Map是一组键值对的结构,具有极快的查找速 ...
- 对于app监测root权限或者强制升级
只做分享学习 以下方法对于大多数Root检测的App (不限于仅银行类App),均适用. 另,"主用户 + Island用户" 情况下: 如果App仅安装在 Island下,当绕不 ...
- RestTemplate+Ribbon实现服务调用
一,通过LoadBalancerClient取得uri,然后RestTemplate 调用 1, 注入LoadBalancerClient @Autowired LoadBalancerClient ...
- CTF相关
https://blog.csdn.net/zxl2016/article/details/96482763
- CopyTranslator安装与使用
PDF 格式的文本,本质上是保证了在大部分设备上都能保持清晰完整的排版格式,但不利于进一步使用,但是 PDF 文档文字复制会包括回车键,文字粘粘和翻译都不方便.通常的做法就是,先转换成 Word 格式 ...
- SP22343 Norma--序列分治
Norma 传送门 题意简化: 定义一个区间的贡献为 \(max*min*len\),求给定序列中所有子区间的总贡献和 题解 考虑 \(O(n*log_2n)\) 的复杂度的做法 数据结构??? yz ...
- Java_枚举
枚举 JDK1.5引入枚举类型, 枚举类型的定义包括枚举的声明和枚举体 enum Season { SPRING, SUMMER, AUTUMN, WINDER } 所有的枚举类型隐性的继承来自jav ...
- 选择API管理平台之前要考虑的5个因素
API(应用程序编程接口)经济的飞速增长导致对API管理平台的需求相应增加. 这些解决方案可在整个生命周期内帮助创建,实施,监控,分析,保护和管理API. 据一些估计,全球API管理市场预计在2018 ...