一级缓存

一级缓存是SqlSession级别的,SqlSessionFactory在openSession的时候会创建一个本地缓存变量。

相关源码

// org.apache.ibatis.session.defaults.DefaultSqlSessionFactory
public SqlSession openSession() {
    return openSessionFromDataSource(configuration.getDefaultExecutorType(), null, false);
}
// 创建SqlSession
private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) {
    Transaction tx = null;
    try {
      final Environment environment = configuration.getEnvironment();
      final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment);
      tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);
      // 创建执行器
      final Executor executor = configuration.newExecutor(tx, execType);
      return new DefaultSqlSession(configuration, executor, autoCommit);
    ...
}
// 根据配置类的执行器类型创建执行器
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;
}
// SimpleExecutor
public SimpleExecutor(Configuration configuration, Transaction transaction) {
    super(configuration, transaction);
}
// BaseExecutor
protected BaseExecutor(Configuration configuration, Transaction transaction) {
    this.transaction = transaction;
    this.deferredLoads = new ConcurrentLinkedQueue<>();
    // 一级缓存
    this.localCache = new PerpetualCache("LocalCache");
    this.localOutputParameterCache = new PerpetualCache("LocalOutputParameterCache");
    this.closed = false;
    this.configuration = configuration;
    this.wrapper = this;
}
// PerpetualCache
public class PerpetualCache implements Cache {
  private final String id;
  private final Map<Object, Object> cache = new HashMap<>();
}
// 执行查询时
public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {
   ...
    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);
      }
    ...
      // LocalCacheScope设置为STATEMENT,一级缓存不会生效
      if (configuration.getLocalCacheScope() == LocalCacheScope.STATEMENT) {
          clearLocalCache();
      }
  return list;
}
// 查询数据库并放入缓存
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);
    }
    localCache.putObject(key, list);
    if (ms.getStatementType() == StatementType.CALLABLE) {
      localOutputParameterCache.putObject(key, parameter);
    }
    return list;
}

说明

  • 默认开启。
  • localCacheScope设置为STATEMENT时,sqlSession执行多次查询时每次查询结束后都会清空一级缓存。
  • 缓存的key与Namespance、SQL语句相关。
  • 一级缓存和执行器类一对一。

二级缓存

与一级缓存的区别

  • 二级缓存是应用级别的缓存, 与Configuration类一对一。
  • 缓存的key是MapperXML的Namespace,Value和一级缓存的类(PerpetualCache)一样。
  • SqlSession事务提交(或关闭)的时候才会存二级缓存,而一级缓存在查询到结果后就存缓存了;但是执行查询时优先从二级缓存中获取查询结果,未命中二级缓存后,才会去一级缓存中获取结果。

启用二级缓存

  • 修改settings
    <setting name="cacheEnabled" value="true" />
  • 修改MapperXML
    <cache />

二级缓存失效

  • 同一命名空间,执行了增删改时,会清空缓存。当存在联合查询时,可以通过设置<cache-ref namespace="" />清空指定命名空间的的二级缓存,但是只能指定一个namespace。

自定义缓存