Mybatis学习笔记(三)-Mybatis中的缓存介绍

Scroll Down

Mybatis中的缓存

一级缓存

mybatis的一级缓存默认是不可关闭的,生命周期与SqlSession同步,在同一个SqlSession对象中,多次查询相同的Sql语句只会查询一次数据库,mybatis查询时会先去查询一级缓存,如果一级缓存不存在就会查询数据库,并将数据库查询出的数据放入一级缓存,一级缓存在PerpetualCache使用HashMap类型集合维护Key和对应的对象,key是使用CacheKey(statementId,分页参数,BoundSql)对象来维护的

一级缓存的生命周期

Mybatis一级缓存的生命周期与SqlSession一致,但是在分布式环境下或者有多个SqlSession的情况下,数据库写会引起脏数据,SqlSession创建的时候会创建对应的缓存对象,在SqlSession执行了增删改方法时,会清空对应的一级缓存,或者执行SqlSession.clearCache()方法也可以清空缓存数据,此时还可以继续操作缓存对象,使用SqlSession.close()方法时将会释放一级缓存,缓存对象不再可用

如何关闭一级缓存

根据源码来查看,一级缓存默认是开启的并且是不可关闭的,只有其中对应的方法进行配置ms.isFlushCacheRequired()为true可以每次执行对应查询方法时清空一级缓存,或者在配置文件中将对应的配置为

LocalCacheScope配置为(默认值是Session)LocalCacheScope.STATEMENT也可以达到相同的效果,每次都会清空缓存

 <settings>
        <!--自动清除一级缓存  -->
        <setting name="localCacheScope" value="STATEMENT"/>
    </settings>
@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--;
    }
    if (queryStack == 0) {
      for (DeferredLoad deferredLoad : deferredLoads) {
        deferredLoad.load();
      }
      // issue #601
      deferredLoads.clear();
      if (configuration.getLocalCacheScope() == LocalCacheScope.STATEMENT) {
        // issue #482
        clearLocalCache();
      }
    }
    return list;
  }

二级缓存

mybatis中的二级缓存逻辑与一级缓存类似,第一次查询时将数据序列化后放入缓存中,第二次查询时直接查询对应缓存并将数据反序列化取出,因为二级缓存是用序列化的方式实现的,所以对应的实体需要实现Serializable接口,一级缓存的作用范围是SqlSession的,二级缓存的作用范围是基于Mapper文件中的namespace的,多个Sqlsession可以共享mapper中的二级缓存区域,多个mapper如果namespace相同也可以共享二级缓存区域

开启二级缓存
<!--开启二级缓存  -->
<settings>
    <setting name="cacheEnabled" value="true"/>
</settings>

或者可以在具体的mapper文件中进行开启缓存

<cache></cache>