mybatis源码解析9---执行器Executor解析

从前面分析我们知道了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   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则对查询的结果进行了缓存处理以及查询的时候会从缓存中进行查询。

原文地址:https://www.cnblogs.com/jackion5/p/10597776.html

时间: 2024-08-03 10:38:35

mybatis源码解析9---执行器Executor解析的相关文章

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

原创作品,可以转载,但是请标注出处地址:http://www.cnblogs.com/V1haoge/p/6724223.html 1.回顾 上面的几篇解析了类型模块,在MyBatis中类型模块包含的就是Java类型与Jdbc类型,和其间的转换处理.类型模块在整个MyBatis功能架构中属于基础组件之一,是提前注册到注册器中,并配置到Configuration中备用. 从这一篇开始解析Parsing解析模块,这个模块不同于Type模块,这个模块更像是一套工具模块.本篇先解析通用标记解析器Gene

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

承接前文Spring源码情操陶冶#task:executor解析器,在前文基础上解析我们常用的spring中的定时任务的节点配置.备注:此文建立在spring的4.2.3.RELEASE版本 附例 Spring中的定时任务基本配置样例如下 <!--create schedule thread pool--> <task:scheduler id="baseScheduler" pool-size="5"></task:scheduler

MyBatis 源码分析 - 配置文件解析过程

* 本文速览 由于本篇文章篇幅比较大,所以这里拿出一节对本文进行快速概括.本篇文章对 MyBatis 配置文件中常用配置的解析过程进行了较为详细的介绍和分析,包括但不限于settings,typeAliases和typeHandlers等,本文的篇幅也主要在对这三个配置解析过程的分析上.下面,我们来一起看一下本篇文章的目录结构. 从目录上可以看出,2.3节.2.5节和2.8节的内容比较多.其中2.3节是关于settings配置解析过程的分析,除了对常规的 XML 解析过程分析,本节额外的分析了元

mybatis源码-解析配置文件(三)之配置文件Configuration解析(超详细, 值得收藏)

1. 简介 1.1 系列内容 本系列文章讲解的是mybatis解析配置文件内部的逻辑, 即 Reader reader = Resources.getResourceAsReader("mybatis-config.xml"); SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(reader); 其背后的逻辑. 1.2 适合对象 了解如何使用 mybatis 来访问数据库.可参看<

Mybatis源码解析(四) —— SqlSession是如何实现数据库操作的?

Mybatis源码解析(四) -- SqlSession是如何实现数据库操作的? ??如果拿一次数据库请求操作做比喻,那么前面3篇文章就是在做请求准备,真正执行操作的是本篇文章要讲述的内容.正如标题一样,本篇文章最最核心的要点就是 SqlSession实现数据库操作的源码解析.但按照惯例,我这边依然列出如下的问题: 1. SqlSession 是如何被创建的? 每次的数据库操作都会创建一个新的SqlSession么?(也许有很多同学会说SqlSession是通过 SqlSessionFactor

mybatis源码追踪1——Mapper方法用法解析

Mapper中的方法执行时会构造为org.apache.ibatis.binding.MapperMethod$MethodSignature对象,从该类源码中可以了解如何使用Mapper方法. [支持的特殊参数类型] RowBounds.ResultHandler.普通参数 (作为sql执行时使用的变量) 其中普通参数可以是单一的model.查询条件的map或直接将一到多个查询条件作为参数(多个条件在框架中最终将封装为map使用) 另外普通参数支持添加@Param注解以修改参数名,如不修改则参

Mybatis源码解析,一步一步从浅入深(六):映射代理类的获取

在文章:Mybatis源码解析,一步一步从浅入深(二):按步骤解析源码中我们提到了两个问题: 1,为什么在以前的代码流程中从来没有addMapper,而这里却有getMapper? 2,UserDao明明是我们定义的一个接口类,根本没有定义实现类,那这个userMapper是什么?是mybatis自动为我们生成的实现类吗? 为了更好的解释着两个问题,我们需要重新认识Configuration这个类. 但是在这之前,你需要了解一个概念(设计模式):JAVA设计模式-动态代理(Proxy)示例及说明

mybatis源码-解析配置文件(四)之配置文件Mapper解析

在 mybatis源码-解析配置文件(三)之配置文件Configuration解析 中, 讲解了 Configuration 是如何解析的. 其中, mappers作为configuration节点的一部分配置, 在本文章中, 我们讲解解析mappers节点, 即 xxxMapper.xml 文件的解析. 1 解析入口 在解析 mybatis-config.xml 时, 会进行解析 xxxMapper.xml 的文件. 在图示流程的 XMLConfigBuilder.parse() 函数中, 该

mybatis源码-解析配置文件(四-1)之配置文件Mapper解析(cache)

相关文章推荐 mybatis 缓存的使用, 看这篇就够了 mybatis源码-解析配置文件(四)之配置文件Mapper解析 1. 简介 本文章主要讲解的是, xxxMapper.xml 文件中, cache 节点的源码. 2. 解析 XMLMapperBuilder.cacheElement() 方法主要负责解析 <cache> private void cacheElement(XNode context) throws Exception { if (context != null) {

MyBatis源码解析之数据源(含数据库连接池简析)

一.概述: 常见的数据源组件都实现了javax.sql.DataSource接口: MyBatis不但要能集成第三方的数据源组件,自身也提供了数据源的实现: 一般情况下,数据源的初始化过程参数较多,比较复杂: 二.设计模式: 为什么要使用工厂模式 数据源模块类图 : 三.MyBatis源码数据源实现 数据结构如下: 使用动态代理封装了真正的数据库连接对象,进行了方法增强:使用前检查连接是否有效,关闭时对连接进行回收: 连接池配置文件配置的数据在这里映射. 用于管理PooledConnection