从前面分析我们知道了sql的具体执行是通过调用SqlSession接口的对应的方法去执行的,而SqlSession最终都是通过调用了自己的Executor对象的query和update去执行的。本文就分析下sql的执行器-----Executor

Executor是mybatis的sql执行器,SqlSession是面向程序的,而Executor则就是面向数据库的,先看下Executor接口的方法有哪些,源码如下:

  1. public interface Executor {
  2.  
  3. ResultHandler NO_RESULT_HANDLER = null;
  4.  
  5. int update(MappedStatement ms, Object parameter) throws SQLException;
  6.  
  7. <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey cacheKey, BoundSql boundSql) throws SQLException;
  8.  
  9. <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException;
  10.  
  11. <E> Cursor<E> queryCursor(MappedStatement ms, Object parameter, RowBounds rowBounds) throws SQLException;
  12.  
  13. List<BatchResult> flushStatements() throws SQLException;
  14.  
  15. void commit(boolean required) throws SQLException;
  16.  
  17. void rollback(boolean required) throws SQLException;
  18.  
  19. CacheKey createCacheKey(MappedStatement ms, Object parameterObject, RowBounds rowBounds, BoundSql boundSql);
  20.  
  21. boolean isCached(MappedStatement ms, CacheKey key);
  22.  
  23. void clearLocalCache();
  24.  
  25. void deferLoad(MappedStatement ms, MetaObject resultObject, String property, CacheKey key, Class<?> targetType);
  26.  
  27. Transaction getTransaction();
  28.  
  29. void close(boolean forceRollback);
  30.  
  31. boolean isClosed();
  32.  
  33. void setExecutorWrapper(Executor executor);

和SqlSession一样定义了各种各样的sql执行的方法,有查询的query方法,有更新的update方法,以及和事务有关的commit方法和rollback方法等,接下来就以query方法为例,看下具体是如何执行的。

Executor接口共有两个实现类,分别是BaseExecutor和CachingExecutor,CachingExecutor是缓存执行器,后面会提到,现在先看下BaseExecutor

BaseExecutor的属性有:

  1. 1 protected Transaction transaction;//事务
  2. protected Executor wrapper;//执行器包装者
  3.  
  4. protected ConcurrentLinkedQueue<DeferredLoad> deferredLoads;//线程安全队列
  5. protected PerpetualCache localCache;//本地缓存
  6. protected PerpetualCache localOutputParameterCache;
  7. protected Configuration configuration;
  8.  
  9. protected int queryStack = 0;//查询次数栈
  10. private boolean closed;//是否已关闭(回滚的时候会被关闭)

再看下BaseExecutor执行query方法的源码:

  1. public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {
  2. ErrorContext.instance().resource(ms.getResource()).activity("executing a query").object(ms.getId());
  3. if (closed) {//判断执行器是否已关闭
  4. throw new ExecutorException("Executor was closed.");
  5. }
  6. //如果查询次数栈为0并且MappedStatement可以清除缓存,则清除本地缓存
  7. if (queryStack == 0 && ms.isFlushCacheRequired()) {
  8. clearLocalCache();
  9. }
  10. List<E> list;
  11. try {
  12. queryStack++;//查询次数+1
  13. list = resultHandler == null ? (List<E>) localCache.getObject(key) : null;//从缓存中根据缓存key查询是否有缓存
  14. if (list != null) {
  15. //如果缓存中有数据,则处理缓存
  16. handleLocallyCachedOutputParameters(ms, key, parameter, boundSql);
  17. } else {
  18. //如果缓存中没有数据,则从数据库查询数据
  19. list = queryFromDatabase(ms, parameter, rowBounds, resultHandler, key, boundSql);
  20. }
  21. } finally {
  22. //查询次数-1
  23. queryStack--;
  24. }
  25. if (queryStack == 0) {
  26. for (DeferredLoad deferredLoad : deferredLoads) {
  27. deferredLoad.load();
  28. }
  29. // issue #601
  30. deferredLoads.clear();
  31. if (configuration.getLocalCacheScope() == LocalCacheScope.STATEMENT) {
  32. // issue #482
  33. clearLocalCache();
  34. }
  35. }
  36. return list;
  37. }

可以看出BaseExecutor会优先从缓存中查询数据,如果缓存不为空再从数据库数据。在这里有一个queryStack会进行自增自减,它的作用是什么呢?

先看下如果没有缓存的话,BaseExecutor是怎么从数据库查询数据的:

  1. private <E> List<E> queryFromDatabase(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {
  2. List<E> list;
  3. localCache.putObject(key, EXECUTION_PLACEHOLDER);
  4. try {
  5. list = doQuery(ms, parameter, rowBounds, resultHandler, boundSql);//执行doQuery方法
  6. } finally {
  7. localCache.removeObject(key);
  8. }
  9. localCache.putObject(key, list);//将查询结果放入缓存
  10. if (ms.getStatementType() == StatementType.CALLABLE) {//如果callable类型查询
  11. localOutputParameterCache.putObject(key, parameter);//将参数放入缓存中
  12. }
  13. return list;
  14. }

可见该方法是调用了doQuery方法从数据库查询了数据,然后将查询的结果及查询用的参数放入了缓存中,而doQuery方法是BaseExecutor中的抽象方法,具体的实现是由BaseExecutor的子类进行实现

  1. protected abstract int doUpdate(MappedStatement ms, Object parameter)
  2. throws SQLException;
  3.  
  4. protected abstract List<BatchResult> doFlushStatements(boolean isRollback)
  5. throws SQLException;
  6.  
  7. protected abstract <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql)
  8. throws SQLException;
  9.  
  10. protected abstract <E> Cursor<E> doQueryCursor(MappedStatement ms, Object parameter, RowBounds rowBounds, BoundSql boundSql)
  11. throws SQLException;
  12.  
  13. protected void closeStatement(Statement statement) {
  14. if (statement != null) {
  15. try {
  16. statement.close();
  17. } catch (SQLException e) {
  18. // ignore
  19. }
  20. }
  21. }

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. C#实体对象出现中文处理乱码的问题

    问题: C#实体对象使用时,对于是中文的增加时,到数据库后变成了?? 解决方法: 增加实体对象时“新建连接”操作中的高级中要设置属性Character Set=urf8  (要手动输入)

  2. JsonResponse返回中文乱码问题

    class Publish(APIView): def get(self, request): publish_list = models.Publish.objects.all() bs = MyS ...

  3. maven配置本地仓库通用

    只要在settings.xml文件中指定仓库就可以了,然后复制仓库到任何地方都可以使用,eclipse中指定一个settings.xml就可以了 仓库的位置是.locks所在目录

  4. Case when then esle end

    Case具有两种格式.简单Case函数和Case搜索函数. --简单Case函数 CASE sex ' THEN '男' ' THEN '女' ELSE '其他' END --Case搜索函数 ' T ...

  5. 15个Node.js项目列表

    前言: Node.js 是一个基于Chrome JavaScript 运行时建立的一个平台,是一个事件驱动I/O服务端JavaScript环境,基于Google的V8引擎,V8引擎执行Javascri ...

  6. python threading acquire release

    线程同步 //test.py import threading import time exitFlag = 0 class myThread (threading.Thread): def __in ...

  7. Kotlin 型变 + 星号投影(扯蛋)

    Kotlin中的型变: 1. in,顾名思义,就是只能作为传入参数的参数类型 2.out, ..............,就是只能作为返回类型参数的参数类型 星号投影: 我们引用官网的吧-- For ...

  8. iOS 正则表达式(二) RegexKitLite使用

    下面介绍的是iOS的一个第三方库,RegexKitLite .使用起来是非常方便的,不过是2008年写的,MAC模式,我们在使用的时候,需要作两步操作. RegexKitLite 导入 我们在gith ...

  9. ThreadLocal源码实现。

    今天在FileInputStream源码中看到有ThreadLocal,之前一直没有理解过这个类,现在进行补充. ThreadLocal即为线程局部变量,它和同步机制处理的是不同的问题域,同步机制是为 ...

  10. apc

    转载(https://www.kancloud.cn/thinkphp/php-best-practices/40866) 使用 APC 在一个标准的 PHP 环境中,每次访问PHP脚本时,脚本都会被 ...