mybatis缓存 - 969251639/study GitHub Wiki

mybatis的缓存分为两种,分别是一级缓存和二级缓存,二级缓存需要依赖第三方缓存,一级缓存则是自带

1. 二级缓存
在mybatis的配置中可以指定开启

<configuration>
    ...
    <settings>
        <setting name="cacheEnabled" value="true" />
        ...
    </settings>
    ...
</configuration>

即是不指定,默认也是开启的

configuration.setCacheEnabled(booleanValueOf(props.getProperty("cacheEnabled"), true));

mybatis二级缓存是通过一个特殊包装的执行器来实现的

  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;
  }

CachingExecutor内部的delegate属性保存了基础执行器的引用

private final Executor delegate;

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

在执行查询操作的时候会先去查询缓存,查询不到就调用执行器查询

@Override
  public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql)
      throws SQLException {
    Cache cache = ms.getCache();//从mapper中获取缓存对象
    if (cache != null) {
      flushCacheIfRequired(ms);
      if (ms.isUseCache() && resultHandler == null) {
        ensureNoOutParams(ms, boundSql);
        @SuppressWarnings("unchecked")
        List<E> list = (List<E>) tcm.getObject(cache, key);
        if (list == null) {
          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);
  }

需要注意的是,这个缓存是全局的,也就是所有session共享

1. 一级缓存
一级缓存和二级缓存不一样,它是session级别,每次查询sql时都会判断一下缓存是否存在(如果开启的话)

  @Override
  public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {
    BoundSql boundSql = ms.getBoundSql(parameter);
    CacheKey key = createCacheKey(ms, parameter, rowBounds, boundSql);//计算缓存键
    return query(ms, parameter, rowBounds, resultHandler, key, boundSql);
 }

  @SuppressWarnings("unchecked")
  @Override
  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.");
    }
    if (queryStack == 0 && ms.isFlushCacheRequired()) {//是否关闭一级缓存
      clearLocalCache();//关闭一级缓存就直接清空缓存
    }
    List<E> list;
    try {
      queryStack++;
      list = resultHandler == null ? (List<E>) localCache.getObject(key) : null;//从缓存获取
      if (list != null) {//缓存命中
        handleLocallyCachedOutputParameters(ms, key, parameter, boundSql);
      } else {//查询数据库
        list = queryFromDatabase(ms, parameter, rowBounds, resultHandler, key, boundSql);
      }
    } finally {
      queryStack--;
    }
    ...
    return list;
  }

另外所有的更新操作都会使缓存失效

  @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();//缓存失效
    return doUpdate(ms, parameter);
  }
⚠️ **GitHub.com Fallback** ⚠️