mybatis——Executor

映射器了解完了,sqlSession.select()实际上是Executor.query(),所以sql执行实际是executor执行的。

一、BaseExecutor——实际的执行器(包含一级缓存实现)

BaseExecutor是一个Executor的顶级抽象类,实现了Executor接口,定义sql执行的执行器。

模板设计模式的运用,将一部分方法逻辑延迟给子类来实现。(专业术语:钩子)

BaseExecutor有4个实现子类:

下面介绍下BaseExecutor的实现

1、变量与构造方法

/* org.apache.ibatis.executor.BaseExecutor */
  protected Transaction transaction;//事务
  protected Executor wrapper;//一个指向自己(this)的指针

  protected ConcurrentLinkedQueue<DeferredLoad> deferredLoads;
  
  //mybatis的一级缓存
  protected PerpetualCache localCache;
  protected PerpetualCache localOutputParameterCache;
  //configuration对象
  protected Configuration configuration;

  protected int queryStack;
  //关闭状态哨兵
  private boolean closed;

  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、主要方法

由于没有注释,所以方法作用不是很清楚,简单分了下类

/* org.apache.ibatis.executor.Executor */
  ResultHandler NO_RESULT_HANDLER = null;

  /* 
   * sql执行方法CRUD
   */
  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;

  /*
   * BatchExecutor:执行所有的statement并关闭
* ReuseExecutor:关闭所有复用的statemet
*/ List<BatchResult> flushStatements() throws SQLException; /* * 事务相关 */ void commit(boolean required) throws SQLException; void rollback(boolean required) throws SQLException; Transaction getTransaction(); /* * 缓存相关 */ 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); /* * 自身状态相关 */ void close(boolean forceRollback); boolean isClosed(); void setExecutorWrapper(Executor executor);

① sql方法执行相关

方法过多,选取一个doQuery执行ff

/* 方法过多,选取一个方法研究 */
/* org.apache.ibatis.executor.BaseExecutor#query(org.apache.ibatis.mapping.MappedStatement, java.lang.Object, org.apache.ibatis.session.RowBounds, org.apache.ibatis.session.ResultHandler) */
  public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {
    //获取sql
    BoundSql boundSql = ms.getBoundSql(parameter);
    //创建缓存key
    CacheKey key = createCacheKey(ms, parameter, rowBounds, boundSql);
    return query(ms, parameter, rowBounds, resultHandler, key, boundSql);
 }

  public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {
    //sql校验是否存在
    ErrorContext.instance().resource(ms.getResource()).activity("executing a query").object(ms.getId());
    //关闭哨兵,如果executor已关闭,抛出异常
    if (closed) {
      throw new ExecutorException("Executor was closed.");
    }
    //清除缓存(mybatis一级缓存)
    //queryStack == 0 :正在执行的select类型的sql的个数
    //并且 mappedStatement的flushCacheRequired属性为true,对应前面<sql>中的flushCache属性默认:<select> : false ; 非<select> true
    if (queryStack == 0 && ms.isFlushCacheRequired()) {
      clearLocalCache();
    }
    List<E> list;
    try {
      queryStack++;
      //根据cacheKey获取缓存
      list = resultHandler == null ? (List<E>) localCache.getObject(key) : null;
      if (list != null) {
        //缓存存在,由于默认preparedStatement,这里相当于空方法
        handleLocallyCachedOutputParameters(ms, key, parameter, boundSql);
      } else {
        //数据库查询
        list = queryFromDatabase(ms, parameter, rowBounds, resultHandler, key, boundSql);
      }
    } finally {
      queryStack--;
    }
    if (queryStack == 0) {
      //延迟加载,什么时候需要延迟加载
      //关联映射时,select返回的resultMap中嵌套了子select查询,延迟加载将select与嵌套的子select查询的结果resultMap关联起来。
      //具体例子 订单查询的结果中嵌套订单详情查询。
      for (DeferredLoad deferredLoad : deferredLoads) {
        deferredLoad.load();
      }
      // issue #601
      deferredLoads.clear();
      if (configuration.getLocalCacheScope() == LocalCacheScope.STATEMENT) {
        // issue #482
        // 一级缓存的作用域是statement级别,清除缓存;默认的是session会话级别,不清楚缓存
        clearLocalCache();
      }
    }
    return list;
  }
list = queryFromDatabase(ms, parameter, rowBounds, resultHandler, key, boundSql):数据库查询
  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);
    } finally {
      localCache.removeObject(key);
    }
    //放入缓存到baseExecutor.localCache中
    localCache.putObject(key, list);
    if (ms.getStatementType() == StatementType.CALLABLE) {
      localOutputParameterCache.putObject(key, parameter);
    }
    return list;
  }

  protected abstract <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql)
      throws SQLException;

查看了下BaseExecutor类,钩子方法只有4个,都是与sql执行相关的方法。

  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;

这里看看各个子类是怎样执行的,还是以doQuery()方法为研究点

SimpleExecutor(默认的BaseExecutor):每创建一个statement,执行完sql后,关闭statement

  public <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {
    Statement stmt = null;
    try {
      //获取configuration对象
      Configuration configuration = ms.getConfiguration();
      //应用configuration的设置--<settings>标签,驼峰命名等
      //另外还会执行拦截器
      StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql);
      stmt = prepareStatement(handler, ms.getStatementLog());
      //sql执行 statement.execute()
      //查询结果处理resultHandler.handleResultSet();result-->resultMap(处理嵌套sql)
      return handler.<E>query(stmt, resultHandler);
    } finally {
      closeStatement(stmt);
    }
  }

  private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException {
    Statement stmt;
    //获取连接
    Connection connection = getConnection(statementLog);
    //创建statement,并设置sql,默认是PreparedStatement
    stmt = handler.prepare(connection, transaction.getTimeout());
    //设置参数parameters
    handler.parameterize(stmt);
    return stmt;
  }

/* org.apache.ibatis.session.Configuration#newStatementHandler */

  public StatementHandler newStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
    //应用configuration的一些设置
    StatementHandler statementHandler = new RoutingStatementHandler(executor, mappedStatement, parameterObject, rowBounds, resultHandler, boundSql);
    //拦截器执行
    statementHandler = (StatementHandler) interceptorChain.pluginAll(statementHandler);
    return statementHandler;
  }

ReuseExecutor:创建一个statement,放入到一个statementMap<sql,statement>中实现statement复用,会设置一个事务超时时间,防止事务一直开启

statement关闭时机:调用doFlushStatement()时,

调用doFlushStatement()时机:① 显式调用:sqlSession.flushStatement(); ② 事务的提交、回退 sqlSession.commit();

/* org.apache.ibatis.executor.ReuseExecutor#doQuery */
  public <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {
    Configuration configuration = ms.getConfiguration();
    StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql);
    Statement stmt = prepareStatement(handler, ms.getStatementLog());
    return handler.<E>query(stmt, resultHandler);
  }

  private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException {
    Statement stmt;
    BoundSql boundSql = handler.getBoundSql();
    String sql = boundSql.getSql();
    if (hasStatementFor(sql)) {
      //statement复用
      stmt = getStatement(sql);
      //事务有效时间
      applyTransactionTimeout(stmt);
    } else {
      Connection connection = getConnection(statementLog);
      stmt = handler.prepare(connection, transaction.getTimeout());
      putStatement(sql, stmt);
    }
    handler.parameterize(stmt);
    return stmt;
  }

  /* 
   * doFlushStatments关闭所有的statement
   * 执行时机:显式调用:sqlSession.flushStatements()
   */
  public List<BatchResult> doFlushStatements(boolean isRollback) throws SQLException {
    for (Statement stmt : statementMap.values()) {
      closeStatement(stmt);
    }
    statementMap.clear();
    return Collections.emptyList();
  }

BatchExecutor:doQuery()创建一个statement,执行完sql,关闭statement,与SimpleExecutor不同主要在于doUpdate方法:

BatchExecutor的doUpdate方法不是立即执行,而是将各种statement放入到一个list中,并且一个statement也可能存储多个sql(执行sql时必须连续,不连续,会生成两个statement)

然后在调用doFlushStatement()时执行所有的statement并且关闭statement:

调用doFlushStatement的时机:① 显式调用:sqlSession.flushStatement() ② 每次执行查询之前。③ 事务提交,sqlSession.commit()

/* org.apache.ibatis.executor.SimpleExecutor#doUpdate 与doQuery没有区别 */
  public int doUpdate(MappedStatement ms, Object parameter) throws SQLException {
    Statement stmt = null;
    try {
      Configuration configuration = ms.getConfiguration();
      StatementHandler handler = configuration.newStatementHandler(this, ms, parameter, RowBounds.DEFAULT, null, null);
      stmt = prepareStatement(handler, ms.getStatementLog());
      return handler.update(stmt);
    } finally {
      closeStatement(stmt);
    }
  }

/* org.apache.ibatis.executor.BatchExecutor#doUpdate */
  public int doUpdate(MappedStatement ms, Object parameterObject) throws SQLException {
    final Configuration configuration = ms.getConfiguration();
    final StatementHandler handler = configuration.newStatementHandler(this, ms, parameterObject, RowBounds.DEFAULT, null, null);
    final BoundSql boundSql = handler.getBoundSql();
    final String sql = boundSql.getSql();
    final Statement stmt;
    if (sql.equals(currentSql) && ms.equals(currentStatement)) {
      //当执行sql,sql与当前sql相同,并且mappedStatement也相同
      //刷新preparedStatement的事务时间
      //追加parameter到preparedStatement中
      //追加resultMap到batchResult中
      //即同一个prepareStatement中放了多个sql,这些sql仅参数不同
      int last = statementList.size() - 1;
      stmt = statementList.get(last);
      applyTransactionTimeout(stmt);
     handler.parameterize(stmt);//fix Issues 322
      BatchResult batchResult = batchResultList.get(last);
      batchResult.addParameterObject(parameterObject);
    } else {
      //初次执行sql时与当前sql或mappedStatement不同时
      //创建connect连接
      //创建statement并设置有效时间
      //记录当前sql、mappedStatement
      //记录添加statement到statementList中
      //记录添加resultMap到batchResultList中
      Connection connection = getConnection(ms.getStatementLog());
      stmt = handler.prepare(connection, transaction.getTimeout());
      handler.parameterize(stmt);    //fix Issues 322
      currentSql = sql;
      currentStatement = ms;
      statementList.add(stmt);
      batchResultList.add(new BatchResult(ms, sql, parameterObject));
    }
    //可以看出是将多个sql放入list实现的队列中(FIFO)
    //并没有执行,执行doFlushStatements()方法时才会执行
  // handler.parameterize(stmt);
    handler.batch(stmt);
    return BATCH_UPDATE_RETURN_VALUE;
  }

  /* 执行doFlushStatements的时机:比ReuseExecutor多一个。
   * 显式调用:sqlSession.flushStatements()
   * 执行doQuery时会先执行doFlushStatements()后查询
* 事务提交:sqlSession.commit();
*/ public List<BatchResult> doFlushStatements(boolean isRollback) throws SQLException { try { List<BatchResult> results = new ArrayList<BatchResult>(); if (isRollback) { return Collections.emptyList(); } for (int i = 0, n = statementList.size(); i < n; i++) { Statement stmt = statementList.get(i); applyTransactionTimeout(stmt); BatchResult batchResult = batchResultList.get(i); try { batchResult.setUpdateCounts(stmt.executeBatch()); MappedStatement ms = batchResult.getMappedStatement(); List<Object> parameterObjects = batchResult.getParameterObjects(); KeyGenerator keyGenerator = ms.getKeyGenerator(); if (Jdbc3KeyGenerator.class.equals(keyGenerator.getClass())) { Jdbc3KeyGenerator jdbc3KeyGenerator = (Jdbc3KeyGenerator) keyGenerator; jdbc3KeyGenerator.processBatch(ms, stmt, parameterObjects); } else if (!NoKeyGenerator.class.equals(keyGenerator.getClass())) { //issue #141 for (Object parameter : parameterObjects) { keyGenerator.processAfter(this, ms, stmt, parameter); } } // Close statement to close cursor #1109 closeStatement(stmt); } catch (BatchUpdateException e) { StringBuilder message = new StringBuilder(); message.append(batchResult.getMappedStatement().getId()) .append(" (batch index #") .append(i + 1) .append(")") .append(" failed."); if (i > 0) { message.append(" ") .append(i) .append(" prior sub executor(s) completed successfully, but will be rolled back."); } throw new BatchExecutorException(message.toString(), e, results, batchResult); } results.add(batchResult); } return results; } finally { for (Statement stmt : statementList) { closeStatement(stmt); } currentSql = null; statementList.clear(); batchResultList.clear(); } }

ResultLoaderMap.ClosedExecutor:ResultLoaderMap的一个私有内部类,仅供ResultLoaderMap对象使用,不支持sql操作,仅close状态控制。

    protected <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {
      throw new UnsupportedOperationException("Not supported.");
    }

 ② 事务相关

  //开启事务
  public Transaction getTransaction() {
    if (closed) {
      throw new ExecutorException("Executor was closed.");
    }
    return transaction;
  }
   
  //提交事务
  public void commit(boolean required) throws SQLException {
    if (closed) {
      throw new ExecutorException("Cannot commit, transaction is already closed");
    }
    //清空缓存
    clearLocalCache();
    //清空statement,BatchExecutor会清空,清空前先执行
    flushStatements();
    if (required) {
      transaction.commit();
    }
  }
  
  //回退
  public void rollback(boolean required) throws SQLException {
    if (!closed) {
      try {
        //清空缓存
        clearLocalCache();
        //清空statement,BatchExecutor不会清空。
        flushStatements(true);
      } finally {
        if (required) {
          transaction.rollback();
        }
      }
    }
  }

③ 缓存相关

/* org.apache.ibatis.executor.BaseExecutor */
  //生成缓存key,
//用sql、parameters、environmentId、mappedStatementId、offset+limit生成一个hashCode
public CacheKey createCacheKey(MappedStatement ms, Object parameterObject, RowBounds rowBounds, BoundSql boundSql) { if (closed) { throw new ExecutorException("Executor was closed."); } CacheKey cacheKey = new CacheKey(); cacheKey.update(ms.getId()); cacheKey.update(rowBounds.getOffset()); cacheKey.update(rowBounds.getLimit()); cacheKey.update(boundSql.getSql()); 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; }  //判断sql是否有缓存 public boolean isCached(MappedStatement ms, CacheKey key) { return localCache.getObject(key) != null; } //清空缓存(mybatis一级缓存) public void clearLocalCache() { if (!closed) { localCache.clear(); localOutputParameterCache.clear(); } }

④ 关闭

  public void close(boolean forceRollback) {
    try {
      try {
        rollback(forceRollback);
      } finally {
        if (transaction != null) {
          transaction.close();
        }
      }
    } catch (SQLException e) {
      // Ignore.  There's nothing that can be done at this point.
      log.warn("Unexpected exception on closing transaction.  Cause: " + e);
    } finally {
      transaction = null;
      deferredLoads = null;
      localCache = null;
      localOutputParameterCache = null;
      closed = true;
    }
  }

  public boolean isClosed() {
    return closed;
  }

  public void setExecutorWrapper(Executor wrapper) {
    this.wrapper = wrapper;
  }

3、总结

① Executor接口实现功能包括四个模块:sql执行模块、事务模块、缓存模块、状态模块(关闭状态)

② 4个功能的具体实现都在BaseExecutor抽象类中,注意以下说的缓存就是mybatis的一级缓存。

  • sql执行模块:先查询缓存,无缓存后执行sql,执行完成后,将结果放入到缓存中,具体执行sql的方法是钩子方法,延迟到子类中。
  • 事务模块:开启事务,事务提交,事务回退。需要注意的是事务提交和事务回退会清空一级缓存,还会清空statement
  • 缓存模块:生成cacheKey,判断是否存在缓存,清空缓存,chachKey的hashCode是多个属性联合生成的,包括id、sql、parameters、offset+limit、environmentId。
  • 状态模块:关闭Executor,清空所有资源;判断关闭状态。

③ 执行sql的具体实现延迟到了4个子类中,注意statement创建之前,会应用configuration对象的设置<setting>,及执行拦截器方法<plugin>中

  • SimpleExecutor:每次执行sql时,创建一个statement,执行sql,关闭statement
  • ReuseExecutor:初次执行sql时,创建一个statement,后续此sql的执行都是用此statement执行,调用doFlushStatement会清空所有statement。(statement复用)
  • BatchExecutor :执行update的sql时,创建一个statement放入到一个队列中,不立即执行,另外如果执行的sql与队尾的statement相同,将sql放入到statement中,注意是队尾statement。在调用doFlushStatement()时才会执行statement。执行query的sql时,先执行doFlushStatement(),然后与SimpleExecutor一致
  • closeExecutor:一个无用的Executor

④ doFlushStatement的时机:

  • SimpleExecutor:由于statement创建执行后直接关闭了,所以doFlushStatement方法无用。
  • ReuseExecutor:显式调用sqlSession.flushStatement;事务提交与回退;Executor关闭
  • BatchExecutor:显式调用sqlSession.flushStatement;事务提交;Executor关闭;每次查询sql执行之前

关联映射时,延迟加载的使用。

二、CachingExecutor——二级缓存执行器

缓存执行器:mybatis二级缓存的实现,下面是CachingExecutor简单结构图

 CachingExecutor声明了一个Executor对象和一个缓存管理对象TransactionCacheManager。

CachingExecutor执行sql时,会先使用先校验二级缓存,后使用BaseExecutor(内含一级缓存)执行sql。

实际使用时:开启一次会话sqlSession时,创建的Executor是new CachingExecutor(new SimpleExecutor);

 1、变量与构造方法

  private final Executor delegate;
  private final TransactionalCacheManager tcm = new TransactionalCacheManager();

  public CachingExecutor(Executor delegate) {
    this.delegate = delegate;
    delegate.setExecutorWrapper(this);
  }

2、方法实现

CachingExecutor的大部分方法都使用策略模式调用内部变量executor的方法。选取几个重要的方法。

①缓存相关方法的实现:

最终调用的都是BaseExecutor的方法

意味着:一级缓存与二级缓存的cacheKey相同;CachingExecutor.clearLocalCache()清理的是一级缓存

/* org.apache.ibatis.executor.CachingExecutor#createCacheKey */
  public CacheKey createCacheKey(MappedStatement ms, Object parameterObject, RowBounds rowBounds, BoundSql boundSql) {
    //BaseExecutor.createCacheKey()
    return delegate.createCacheKey(ms, parameterObject, rowBounds, boundSql);
  }
  //调用baseExecutor.isCached
  public boolean isCached(MappedStatement ms, CacheKey key) {
    return delegate.isCached(ms, key);
  }
  //调用baseExecutor.clearLocalCache():注意这里是清空一级缓存
  public void clearLocalCache() {
    delegate.clearLocalCache();
  }

② 查询方法

  public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {
    //跟一级缓存相比,生成的boundSql一致,生成的cacheKey一致
    BoundSql boundSql = ms.getBoundSql(parameterObject);
    CacheKey key = createCacheKey(ms, parameterObject, rowBounds, boundSql);
    return query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
  }

  public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql)
      throws SQLException {
    //获取二级缓存,二级缓存对应mappedStatement.cache
//前面<mapper>标签解析时漏掉了,<mapper>里有<cache>标签
//在解析<cache>标签时会创建一个cache对象放入到configuration.caches中同时会关联到mapperStatement.cache
//二级缓存有多个cache实例,一个mapper.xml对应一个cache实例
Cache cache = ms.getCache(); if (cache != null) { //mappedStatement.flushCacheRequired == true ,删除缓存mappedStatement.cache.clear() //复习下mappedStatement.flushCacheRequired,对应<sql>上的flushCache属性,默认<select>:false;非<select>:true flushCacheIfRequired(ms); //mappedStatement.useCache == true时才使用缓存 //复习下mappedStatement.userCache,对应<sql>上的useCache属性,默认<select>:true;非<select>:false if (ms.isUseCache() && resultHandler == null) { ensureNoOutParams(ms, boundSql); @SuppressWarnings("unchecked") //从缓存中获取cacheKey对应的value //mappedStatement.cache.get(cacheKey) List<E> list = (List<E>) tcm.getObject(cache, key); if (list == null) { //缓存中没有调用BaseExecutor执行sql,这里会查询一级缓存,若不存在,查询数据库,并更新一级缓存 list = delegate.<E> query(ms, parameterObject, rowBounds, resultHandler, key, boundSql); //放入二级缓存中 tcm.putObject(cache, key, list); // issue #578 and #116 } return list; } } return delegate.<E> query(ms, parameterObject, rowBounds, resultHandler, key, boundSql); }

3、总结

① cacheExecutor采用策略模式,组合了一个BaseExecutor,执行的方法也是BaseExecutor的方法

② 由于CachingExecutor.createCacheKey()最终调用的是BaseExecutor.createCacheKey,所以一级缓存与二级缓存的cacheKey相同

③ 开启二级缓存后,sql执行先从查询二级缓存,后查询一级缓存,最后查询数据库。

④ 一级缓存 : BaseExecutor.localCache;二级缓存 : configuration.caches<namespace,cache>以namespace;两种cache最终数据存储在PerpetualCache.cache(Map类型)中

⑤ 虽然configuration默认的Executor是new CachingExecutor(new SimpleExecutor),但是由于在解析mapper.xml时,有<cache>或<cache-ref>标签才会创建一个cache到configuration.caches中,所以二级缓存默认是关闭的,需要到mapper.xml里面加入<cache><cache-ref>开启二级缓存

⑥ <cache>与<cache-ref>:<cache>创建一个LruCache实例,管理二级缓存的开启关闭,里面有一个PerpetualCache类型变量(装饰者模式)<cache-ref>作用主要是:一个mapper.xml可以引用另一mapper.xml的缓存达到缓存共享。

补充:LruCache与CachingExecutor的UML图类似,为什么判断一个是装饰者模式,一个是策略模式。

装饰者模式设计目的是原对象(PerpetualCache)的功能增强;策略默认设计目的原对象(Executor)抽象,可使用多个子对象(SimpleExecutor、ReuseExecutor、BatchExecutor)替换

posted on 2020-03-09 17:14  FFStayF  阅读(505)  评论(0编辑  收藏  举报