前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Mybatis源码分析之-Executor

Mybatis源码分析之-Executor

作者头像
用户4919348
发布2019-05-10 14:52:21
4620
发布2019-05-10 14:52:21
举报
文章被收录于专栏:波波烤鸭波波烤鸭

  mybatis的源代码相对于spring的来说简单了很多,对于初学者,可以先了解了mybatis的源码后再去了解spring的源码,本文主要来分析下Executor的内容

Executor介绍

  Executor是mybatis的一个核心接口,所有的Mapper语句的执行都是通过Executor进行的。类结构图如下

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

1.Executor(顶层接口)

  父接口,在此接口中定义了各种处理方法。具体如下:

代码语言:javascript
复制
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;
  // 刷新statements
  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 对象
  Transaction getTransaction();
	// 关闭
  void close(boolean forceRollback);
	// 是否关闭
  boolean isClosed();
	// 设置执行器增强器
  void setExecutorWrapper(Executor executor);
}

2.BaseExecutor

  BaseExecutor是一个抽象类,采用模板方法的设计模式。它实现了Executor接口,实现了执行器的基本功能。具体使用哪一个Executor则是可以在 mybatis 的 config.xml 中进行配置的。默认为SimpleExecutor;

代码语言:javascript
复制
<settings>
    <!--SIMPLE、REUSE、BATCH-->
    <setting name="defaultExecutorType" value="SIMPLE"/>
</settings>

2.1构造方法

  子类的构造方法会调用 BaseExecutor 的构造方法。默认都支持一级缓存;

代码语言:javascript
复制
  protected BaseExecutor(Configuration configuration, Transaction transaction) {
    this.transaction = transaction;
    this.deferredLoads = new ConcurrentLinkedQueue<DeferredLoad>();
    // 一级缓存
    this.localCache = new PerpetualCache("LocalCache");
    this.localOutputParameterCache = new PerpetualCache("LocalOutputParameterCache");
    this.closed = false;
    this.configuration = configuration;
    this.wrapper = this;
  }

2.2 update方法

  BaseExecutor中实现了update方法,改方法执行DML操作的时候都会执行,通过源码我们会发现,执行DML操作前会清空一级缓存

代码语言:javascript
复制
  @Override
  public int update(MappedStatement ms, Object parameter) throws SQLException {
    ErrorContext.instance().resource(ms.getResource()).activity("executing an update").object(ms.getId());
    if (closed) {
      throw new ExecutorException("Executor was closed.");
    }
    // 清空一级缓存
    clearLocalCache();
    // 调用doUpdate方法执行
    return doUpdate(ms, parameter);
  }

2.3 query方法

  查询操作会先在缓存中查询,缓存命中失败后再去数据中查询。

在这里插入图片描述
在这里插入图片描述

2.4 createCacheKey

  查看缓存key的生成。了解cacheKey的组成。一级缓存通过 HashMap 实现,它的键对象根据SQL的ID,参数,SQL本身,分页参数以及JDBC的参数信息构成。

代码语言:javascript
复制
@Override
  // 创建CacheKey对象
  public CacheKey createCacheKey(MappedStatement ms, Object parameterObject, RowBounds rowBounds, BoundSql boundSql) {
    if (closed) {
      throw new ExecutorException("Executor was closed.");
    }
    CacheKey cacheKey = new CacheKey();
    // MappedStatement的id
    cacheKey.update(ms.getId());
    // 分页参数的offset
    cacheKey.update(rowBounds.getOffset());
    // 分页参数的limit
    cacheKey.update(rowBounds.getLimit());
    // SQL语句本身
    cacheKey.update(boundSql.getSql());
    // 传递给jdbc的参数
    List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
    TypeHandlerRegistry typeHandlerRegistry = ms.getConfiguration().getTypeHandlerRegistry();
    // mimic DefaultParameterHandler logic
    for (ParameterMapping parameterMapping : parameterMappings) {
      if (parameterMapping.getMode() != ParameterMode.OUT) {
        Object value;
        String propertyName = parameterMapping.getProperty();
        if (boundSql.hasAdditionalParameter(propertyName)) {
          value = boundSql.getAdditionalParameter(propertyName);
        } else if (parameterObject == null) {
          value = null;
        } else if (typeHandlerRegistry.hasTypeHandler(parameterObject.getClass())) {
          value = parameterObject;
        } else {
          MetaObject metaObject = configuration.newMetaObject(parameterObject);
          value = metaObject.getValue(propertyName);
        }
        cacheKey.update(value);
      }
    }
    if (configuration.getEnvironment() != null) {
      // issue #176
      cacheKey.update(configuration.getEnvironment().getId());
    }
    return cacheKey;
  }

2.5 定义的抽象方法

  定义的抽象方法有如下几个,交给实现类来实现。

代码语言:javascript
复制
// 定义的四个抽象方法,在去掉 do 前缀的相应方法中被调用
  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;

BaseExecutor的实现类是在Configuration中创建出来的

代码语言:javascript
复制
  public Executor newExecutor(Transaction transaction, ExecutorType executorType) {
    executorType = executorType == null ? defaultExecutorType : executorType;
    executorType = executorType == null ? ExecutorType.SIMPLE : executorType;
    Executor executor;
    if (ExecutorType.BATCH == executorType) {
      executor = new BatchExecutor(this, transaction);
    } else if (ExecutorType.REUSE == executorType) {
      executor = new ReuseExecutor(this, transaction);
    } else {
      executor = new SimpleExecutor(this, transaction);
    }
    if (cacheEnabled) {
      executor = new CachingExecutor(executor);
    }
    // 如果有拦截器 则会返回对应的代理类
    executor = (Executor) interceptorChain.pluginAll(executor);
    return executor;
  }

3.SimpleExecutor

  最简单的执行器,根据对应的sql直接执行即可,不会做一些额外的操作;拼接完SQL之后,直接交给 StatementHandler去执行。   每执行一次update或select,就开启一个Statement对象,用完立刻关闭Statement对象。(可以是Statement或PrepareStatement对象) 我们选择doUpdate()方法来看下

代码语言:javascript
复制
 @Override
 public int doUpdate(MappedStatement ms, Object parameter) throws SQLException {
   Statement stmt = null;
   try {
     Configuration configuration = ms.getConfiguration();
     // 获取Statement处理器对象
     StatementHandler handler = configuration.newStatementHandler(this, ms, parameter, RowBounds.DEFAULT, null, null);
     // 获取具体的Statement对象
     stmt = prepareStatement(handler, ms.getStatementLog());
     // 处理器执行更新操作
     return handler.update(stmt);
   } finally {
     closeStatement(stmt);
   }
 }

prepareStatement方法

代码语言:javascript
复制
  private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException {
    Statement stmt;
    // 获取连接通道
    Connection connection = getConnection(statementLog);
    // 创建Statement对象
    stmt = handler.prepare(connection, transaction.getTimeout());
    handler.parameterize(stmt);
    return stmt;
  }

prepare方法

在这里插入图片描述
在这里插入图片描述

所以BaseExecutor中具体的Statement可以是这三种情况。

4.BatchExecutor

  执行update(没有select,JDBC批处理不支持select),将所有sql都添加到批处理中(addBatch()),等待统一执行(executeBatch()),它缓存了多个Statement对象,每个Statement对象都是addBatch()完毕后,等待逐一执行executeBatch()批处理的;可以是Statement或PrepareStatement对象.

5.ReuseExecutor

  可重用的执行器,重用的对象是Statement,也就是说该执行器会缓存同一个sql的Statement,省去Statement的重新创建,优化性能。内部的实现是通过一个HashMap来维护Statement对象的。由于当前Map只在该session中有效,所以使用完成后记得调用flushStatements来清除Map。

代码语言:javascript
复制
public class ReuseExecutor extends BaseExecutor {


  private final Map<String, Statement> statementMap = new HashMap<String, Statement>();
  
  // 调用父类构造器
  public ReuseExecutor(Configuration configuration, Transaction transaction) {
    super(configuration, transaction);
  }

  
  private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException {
    Statement stmt;
    BoundSql boundSql = handler.getBoundSql();
    String sql = boundSql.getSql();
    if (hasStatementFor(sql)) {
        // 如果缓存了该SQL,则返回其Statement对象
      stmt = getStatement(sql);
      applyTransactionTimeout(stmt);
    } else {
        // 如果没有缓存该SQL,则创建SQL的Statement,并加入缓存
      Connection connection = getConnection(statementLog);
      stmt = handler.prepare(connection, transaction.getTimeout());
      putStatement(sql, stmt);
    }
    handler.parameterize(stmt);
    return stmt;
  }

  // 是否缓存了这个 sql
  private boolean hasStatementFor(String sql) {
    try {
      return statementMap.keySet().contains(sql) && !statementMap.get(sql).getConnection().isClosed();
    } catch (SQLException e) {
      return false;
    }
  }
  
  // 返回指定sql的 Statement
  private Statement getStatement(String s) {
    return statementMap.get(s);
  }

  // 添加SQL和Statement
  private void putStatement(String sql, Statement stmt) {
    statementMap.put(sql, stmt);
  }

}

6.CachingExecutor

  启用于二级缓存时的执行器;采用静态代理;代理一个 Executor 对象。执行 update 方法前判断是否清空二级缓存;执行 query 方法前先在二级缓存中查询,命中失败再通过被代理类查询。

代码语言:javascript
复制
public class CachingExecutor implements Executor {
    // 持有的 Executor,最终的操作都由该对象实现
    private final Executor delegate;
    private final TransactionalCacheManager tcm = new TransactionalCacheManager();

    public CachingExecutor(Executor delegate) {
        this.delegate = delegate;
        delegate.setExecutorWrapper(this);
    }
    
    public int update(MappedStatement ms, Object parameterObject) throws SQLException {
        this.flushCacheIfRequired(ms);
        return this.delegate.update(ms, parameterObject);
    }
    
    public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {
        Cache cache = ms.getCache();
        if (cache != null) {
            this.flushCacheIfRequired(ms);
            if (ms.isUseCache() && resultHandler == null) {
                this.ensureNoOutParams(ms, boundSql);
                List<E> list = (List)this.tcm.getObject(cache, key);
                if (list == null) {
                    list = this.delegate.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
                    this.tcm.putObject(cache, key, list);
                }

                return list;
            }
        }

        return this.delegate.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
    }
        // 是否清空二级缓存
        private void flushCacheIfRequired(MappedStatement ms) {
        Cache cache = ms.getCache();
        if (cache != null && ms.isFlushCacheRequired()) {
            this.tcm.clear(cache);
        }

    }
}
本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2019年04月27日,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • Executor介绍
    • 1.Executor(顶层接口)
      • 2.BaseExecutor
        • 2.1构造方法
        • 2.2 update方法
        • 2.3 query方法
        • 2.4 createCacheKey
        • 2.5 定义的抽象方法
      • 3.SimpleExecutor
        • 4.BatchExecutor
          • 5.ReuseExecutor
            • 6.CachingExecutor
            相关产品与服务
            批量计算
            批量计算(BatchCompute,Batch)是为有大数据计算业务的企业、科研单位等提供高性价比且易用的计算服务。批量计算 Batch 可以根据用户提供的批处理规模,智能地管理作业和调动其所需的最佳资源。有了 Batch 的帮助,您可以将精力集中在如何分析和处理数据结果上。
            领券
            问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档