zoukankan      html  css  js  c++  java
  • 六、MyBatis-缓存机制

    MyBatis 包含一个非常强大的查询缓存特性,它可以非常方便地配置和定制。缓存可以极大的提升查询效率。
    MyBatis系统中默认定义了两级缓存, 一级 缓存和 二级缓存。
    – 1、默认情况下,只有一级缓存(SqlSession级别的缓存,也称为本地缓存)开启,一级缓存默认实现类org.apache.ibatis.cache.impl.PerpetualCache。
    – 2、二级缓存需要手动开启和配置,他是基于namespace级别的缓存。
    – 3、为了提高扩展性。MyBatis定义了缓存接口Cache,我们可以通过实现Cache接口来自定义二级缓存。

    一级缓存

    一级缓存是Local Cache,即本地缓存,默认作用域为SqlSesson级别,mybatis3.1之后, 可以配置本地缓存的作用域(SESSION|STATEMENT),在全局配置文件中可以设置localCacheScope。Mybatis默认的一级缓存默认是通过org.apache.ibatis.cache.impl.PerpetualCache来实现的,PerpetualCache内里有一个HashMap,用来存储本地缓存。

    Map的CacheKey由方法的ID,RowBounds,SQL语句,参数,环境因素影响;Map的Value存放具体的结果对象。

    格式:影响因子的HashCode1:影响因子的HashCode2:方法ID:offset:limit:SQL语句:参数:环境ID

    -1623117942:1735139101:com.kancy.mapper.PersonMapper.selectPersonById:0:2147483647:select * from t_person where id = ? and last_name = ? and sex = ?:1:emma:0:test

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

    从查询的源码可以看出,mybatis在查询前,先看是否需要清除缓存,再通过CacheKey从缓存中查找是否有对应的key,有则返回对象,无则从数据库中查找,再看一级缓存的作用域范围,是STATEMENT则清除缓存,否则将结果集放到缓存中。

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

     

    一级缓存演示& & 失效情况

    同一次会话期间只要查询过的数据都会保存在当前SqlSession的一个Map中
    一级缓存失效的四种情况
    – 1、不同的SqlSession对应不同的一级缓存
    – 2、同一个SqlSession但是查询条件不同
    – 3、同一个SqlSession两次查询期间执行了任何一次增删改操作
    – 4、同一个SqlSession两次查询期间手动清空了缓存

    注意:

    – sql标签的flushCache属性查询默认flushCache=false;增删改insert|update|delete会修改数据,默认flushCache=true,会同时清空一级和二级缓存。
    – sqlSession.clearCache():只是用来清除一级缓存,不会清除二级缓存。
    – 当在某一个作用域 (一级缓存Session/二级缓存Namespaces) 进行了 C/U/D 操作后,默认该作用域下所有有select中的缓存将被clear。

    二级缓存

    kancy
  • 相关阅读:
    WPF
    Xamarin.Forms + Prism,整理页面导航跳转流程
    Xamarin.Forms踩坑整理
    整理
    WPF
    LINQ中,Single()、SingleOrDefault()的解析、示例
    InstallShield打包
    Centos7安装jexus,部署asp.net core,asp.net mvc
    c#修改webConfig的内容
    .net core 3.1简单swagger配置教程
  • 原文地址:https://www.cnblogs.com/kancy/p/10223025.html
Copyright © 2011-2022 走看看