mybatis源码解析9---执行器Executor解析
从前面分析我们知道了sql的具体执行是通过调用SqlSession接口的对应的方法去执行的,而SqlSession最终都是通过调用了自己的Executor对象的query和update去执行的。本文就分析下sql的执行器-----Executor
Executor是mybatis的sql执行器,SqlSession是面向程序的,而Executor则就是面向数据库的,先看下Executor接口的方法有哪些,源码如下:
public interface Executor { ResultHandler NO_RESULT_HANDLER = null; int update(MappedStatement ms, Object parameter) throws SQLException; <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey cacheKey, BoundSql boundSql) throws SQLException; <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException; <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 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(); void setExecutorWrapper(Executor executor);
和SqlSession一样定义了各种各样的sql执行的方法,有查询的query方法,有更新的update方法,以及和事务有关的commit方法和rollback方法等,接下来就以query方法为例,看下具体是如何执行的。
Executor接口共有两个实现类,分别是BaseExecutor和CachingExecutor,CachingExecutor是缓存执行器,后面会提到,现在先看下BaseExecutor
BaseExecutor的属性有:
1 protected Transaction transaction;//事务
protected Executor wrapper;//执行器包装者 protected ConcurrentLinkedQueue<DeferredLoad> deferredLoads;//线程安全队列
protected PerpetualCache localCache;//本地缓存
protected PerpetualCache localOutputParameterCache;
protected Configuration configuration; protected int queryStack = 0;//查询次数栈
private boolean closed;//是否已关闭(回滚的时候会被关闭)
再看下BaseExecutor执行query方法的源码:
public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {
ErrorContext.instance().resource(ms.getResource()).activity("executing a query").object(ms.getId());
if (closed) {//判断执行器是否已关闭
throw new ExecutorException("Executor was closed.");
}
//如果查询次数栈为0并且MappedStatement可以清除缓存,则清除本地缓存
if (queryStack == 0 && ms.isFlushCacheRequired()) {
clearLocalCache();
}
List<E> list;
try {
queryStack++;//查询次数+1
list = resultHandler == null ? (List<E>) localCache.getObject(key) : null;//从缓存中根据缓存key查询是否有缓存
if (list != null) {
//如果缓存中有数据,则处理缓存
handleLocallyCachedOutputParameters(ms, key, parameter, boundSql);
} else {
//如果缓存中没有数据,则从数据库查询数据
list = queryFromDatabase(ms, parameter, rowBounds, resultHandler, key, boundSql);
}
} finally {
//查询次数-1
queryStack--;
}
if (queryStack == 0) {
for (DeferredLoad deferredLoad : deferredLoads) {
deferredLoad.load();
}
// issue #601
deferredLoads.clear();
if (configuration.getLocalCacheScope() == LocalCacheScope.STATEMENT) {
// issue #482
clearLocalCache();
}
}
return list;
}
可以看出BaseExecutor会优先从缓存中查询数据,如果缓存不为空再从数据库数据。在这里有一个queryStack会进行自增自减,它的作用是什么呢?
先看下如果没有缓存的话,BaseExecutor是怎么从数据库查询数据的:
private <E> List<E> queryFromDatabase(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {
List<E> list;
localCache.putObject(key, EXECUTION_PLACEHOLDER);
try {
list = doQuery(ms, parameter, rowBounds, resultHandler, boundSql);//执行doQuery方法
} finally {
localCache.removeObject(key);
}
localCache.putObject(key, list);//将查询结果放入缓存
if (ms.getStatementType() == StatementType.CALLABLE) {//如果callable类型查询
localOutputParameterCache.putObject(key, parameter);//将参数放入缓存中
}
return list;
}
可见该方法是调用了doQuery方法从数据库查询了数据,然后将查询的结果及查询用的参数放入了缓存中,而doQuery方法是BaseExecutor中的抽象方法,具体的实现是由BaseExecutor的子类进行实现
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; protected void closeStatement(Statement statement) {
if (statement != null) {
try {
statement.close();
} catch (SQLException e) {
// ignore
}
}
}
BaseExecutor共有SimpleExecutor、ReuseExecutor、BatchExecutor以及ClosedExecutor四个子类,这个后面再分析,现在我们以及知道了SqlSession是调用了Executor的方法来执行sql,而Executor的默认实现类的BaseExecutor,而BaseExecutor又是调用了其子类的方法。而BaseExecutor则对查询的结果进行了缓存处理以及查询的时候会从缓存中进行查询。
mybatis源码解析9---执行器Executor解析的更多相关文章
- 浩哥解析MyBatis源码(十一)——Parsing解析模块之通用标记解析器(GenericTokenParser)与标记处理器(TokenHandler)
原创作品,可以转载,但是请标注出处地址:http://www.cnblogs.com/V1haoge/p/6724223.html 1.回顾 上面的几篇解析了类型模块,在MyBatis中类型模块包含的 ...
- Spring源码情操陶冶#task:executor解析器
承接Spring源码情操陶冶-自定义节点的解析.线程池是jdk的一个很重要的概念,在很多的场景都会应用到,多用于处理多任务的并发处理,此处借由spring整合jdk的cocurrent包的方式来进行深 ...
- mybatis源码分析之02配置文件解析
该篇正式开始学习mybatis的源码,本篇主要学习mybatis是如何加载配置文件mybatis-config.xml的, 先从测试代码入手. public class V1Test { public ...
- mybatis源码阅读-MappedStatement各个属性解析过程(八)
调用方 类org.apache.ibatis.builder.xml.XMLMapperBuilder private void configurationElement(XNode context) ...
- Mybatis源码分析之Mapper文件解析
感觉CSDN对markdown的支持不够友好,总是伴随各种问题,很恼火! xxMapper.xml的解析主要由XMLMapperBuilder类完成,parse方法来完成解析: public void ...
- Mybatis源码阅读-配置文件及映射文件解析
Mybatis源码分析: 1.配置文件解析: 1.1源码阅读入口: org.apache.ibatis.builder.xml.XMLConfigBuilder.parse(); 功能:解析全局配置文 ...
- MyBatis源码 核心配置解析 properties元素
XMLConfigBuilder的parseConfiguration(XNode)方法,用于解析配置文件 XMLConfigBuilder的propertiesElement(XNode)方法,用于 ...
- Spring源码情操陶冶#task:scheduled-tasks解析器
承接前文Spring源码情操陶冶#task:executor解析器,在前文基础上解析我们常用的spring中的定时任务的节点配置.备注:此文建立在spring的4.2.3.RELEASE版本 附例 S ...
- mybatis源码学习(一) 原生mybatis源码学习
最近这一周,主要在学习mybatis相关的源码,所以记录一下吧,算是一点学习心得 个人觉得,mybatis的源码,大致可以分为两部分,一是原生的mybatis,二是和spring整合之后的mybati ...
- mybatis源码分析之06二级缓存
上一篇整合redis框架作为mybatis的二级缓存, 该篇从源码角度去分析mybatis是如何做到的. 通过上一篇文章知道,整合redis时需要在FemaleMapper.xml中添加如下配置 &l ...
随机推荐
- html多文件上传,可支持预览
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...
- 为什么mysql 5.7.24启停不显示错误信息?log-error_verbosity参数
关键词:log-error_verbosity ,mysql启停没有信息,mysql启停不显示错误信息,mysql不显示启停信息 原因就是因为 log-error_verbosity = 2 被设置成 ...
- wamp支持win10吗?怎么设置?
上周ytkah总算把系统升级到win10了,可怎么设置wamp支持win10呢?启动wampwerver是处于黄色状态,打开本地页面是空白,应该是端口问题. 单击右下角wamp图标,点Apache,修 ...
- Python3学习之路~0 目录
目录 Python3学习之路~2.1 列表.元组操作 Python3学习之路~2.2 简单的购物车程序 Python3学习之路~2.3 字符串操作 Python3学习之路~2.4 字典操作 Pytho ...
- 在Windows Server 2008的桌面上显示“我的电脑”“网上邻居”等图标?
装完windows server2008 r2 x64后发现桌面只有一个“回收站”图标,如何把“我的电脑”“网上邻居”等图标添加到桌面呢?操作步骤: 1. 点击 开始 在搜索中输入 icon 2. 点 ...
- javascript篇-知道value值,返回对象中的key值
前提是,value值是唯一的,业务中有这样一个需求,代码如下:
- secure CRT常用的查看服务器日志命令
tail -f /(日志文件目录) 查看日志命令ctrl+c停止打印ls -al 查看文件的详细信息路径等vi /(日志文件目录) 打开日志/搜索字符退出:按ESC后,按“:”,输入q, ...
- 新手详解JAVA+数据库+JSP完成简单页面
本篇以数据库添加为例(本例中数据库名为“xinxi”表单名字为“stud”) 准备---实体层: package entity; public class Student { private Stri ...
- python math random
很有用个的工具 值得好好看看,这是作者(python发明者)对于工作中使用到的大多数场景的提炼 //test.py 1 import math 2 3 print abs(-10) 4 print m ...
- python的赋值,深拷贝和浅拷贝的区别
原文地址https://www.cnblogs.com/xueli/p/4952063.html 赋值:a = [1,2,3,["a","b"]] b=a,那 ...