从前面分析我们知道了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解析的更多相关文章

  1. 浩哥解析MyBatis源码(十一)——Parsing解析模块之通用标记解析器(GenericTokenParser)与标记处理器(TokenHandler)

    原创作品,可以转载,但是请标注出处地址:http://www.cnblogs.com/V1haoge/p/6724223.html 1.回顾 上面的几篇解析了类型模块,在MyBatis中类型模块包含的 ...

  2. Spring源码情操陶冶#task:executor解析器

    承接Spring源码情操陶冶-自定义节点的解析.线程池是jdk的一个很重要的概念,在很多的场景都会应用到,多用于处理多任务的并发处理,此处借由spring整合jdk的cocurrent包的方式来进行深 ...

  3. mybatis源码分析之02配置文件解析

    该篇正式开始学习mybatis的源码,本篇主要学习mybatis是如何加载配置文件mybatis-config.xml的, 先从测试代码入手. public class V1Test { public ...

  4. mybatis源码阅读-MappedStatement各个属性解析过程(八)

    调用方 类org.apache.ibatis.builder.xml.XMLMapperBuilder private void configurationElement(XNode context) ...

  5. Mybatis源码分析之Mapper文件解析

    感觉CSDN对markdown的支持不够友好,总是伴随各种问题,很恼火! xxMapper.xml的解析主要由XMLMapperBuilder类完成,parse方法来完成解析: public void ...

  6. Mybatis源码阅读-配置文件及映射文件解析

    Mybatis源码分析: 1.配置文件解析: 1.1源码阅读入口: org.apache.ibatis.builder.xml.XMLConfigBuilder.parse(); 功能:解析全局配置文 ...

  7. MyBatis源码 核心配置解析 properties元素

    XMLConfigBuilder的parseConfiguration(XNode)方法,用于解析配置文件 XMLConfigBuilder的propertiesElement(XNode)方法,用于 ...

  8. Spring源码情操陶冶#task:scheduled-tasks解析器

    承接前文Spring源码情操陶冶#task:executor解析器,在前文基础上解析我们常用的spring中的定时任务的节点配置.备注:此文建立在spring的4.2.3.RELEASE版本 附例 S ...

  9. mybatis源码学习(一) 原生mybatis源码学习

    最近这一周,主要在学习mybatis相关的源码,所以记录一下吧,算是一点学习心得 个人觉得,mybatis的源码,大致可以分为两部分,一是原生的mybatis,二是和spring整合之后的mybati ...

  10. mybatis源码分析之06二级缓存

    上一篇整合redis框架作为mybatis的二级缓存, 该篇从源码角度去分析mybatis是如何做到的. 通过上一篇文章知道,整合redis时需要在FemaleMapper.xml中添加如下配置 &l ...

随机推荐

  1. FastDFS的使用

    1.FastDFS 1.1. 什么是FastDFS? FastDFS是用c语言编写的一款开源的分布式文件系统.FastDFS为互联网量身定制,充分考虑了冗余备份.负载均衡.线性扩容等机制,并注重高可用 ...

  2. finecms同时调用子栏目和子栏目的文章怎么操作

    之前ytkah写过dedecms如何调用当前栏目的子栏目及子栏目文章,那如果是finecms如何同时调用子栏目和子栏目的文章呢? {list action=category pid=0 id=31} ...

  3. 将 java 改写成 beanshell 的经验之谈

    下面经验仅仅针对 bsh for android 而谈, PC 上 beanshell 无需这样改. public class TimeTest  改写为闭包: TimeTest()  闭包末尾添加语 ...

  4. Redis入门到高可用(六)—— 字符串

    一.结构和命令 1.字符串键值结构 key是字符串,value可以是字符串.数字.二进制.json等: redis的key和string类型value限制均为512MB. 2.使用场景 ♦️ 缓存 ♦ ...

  5. LigerUi遮罩的两个方法

    $.ligerDialog.waitting('正在查询,请稍候...'); $.ligerDialog.close();

  6. os.path.join路径拼接

    #import os print("0:", os.path.join('/aaa', 'bbb', 'ccc.txt')) #0: /aaa\bbb\ccc.txt 多数这种用法 ...

  7. DL中epoch、batch等的意义【转载】

    转自:深度学习中 number of training epochs 中的 epoch到底指什么? - 知乎 https://www.zhihu.com/question/43673341 1. (1 ...

  8. powerdesign连接Oracle&Mysql

    Oracle部分 想用powerDesign,需要用到oracle数据库,记录配置过程 1,安装win64_11gR2_client,选择安装方式为管理员,按默认选安装,过程大概几分钟就好 2,配置客 ...

  9. [Java in NetBeans] Lesson 13. Multidimensional Arrays

    这个课程的参考视频和图片来自youtube. 主要学到的知识点有: 1. Multidimensional Array: Array that has more than one dimension. ...

  10. Python list 和 tuple 使用小记

    list和tuple是Python内置的有序集合,一个可变,一个不可变.根据需要来选择使用它们. 1.内置数据类型,列表List >>> appleVersion = ['apple ...