美文网首页
mybatis源码之二 缓存

mybatis源码之二 缓存

作者: 左洁 | 来源:发表于2019-08-12 20:01 被阅读0次

    mybatis源码之二 缓存

    1:一级缓存设置

    1.1:mybatis-config文件配置

    mybatis-config.png

    1.2: 测试代码

    cache-test.png

    1.3:测试结果

    mybatis-result.png

    代码执行两个相同查询语句,mybatis日志打印一次,第二次查询走了缓存。

    2:从源码分析同一个session如何走缓存,分析如下

    session.selectOne(…………) ->
        org.apache.ibatis.session.defaults.DefaultSqlSession#selectOne(…………)->
         ………………
         org.apache.ibatis.session.defaults.DefaultSqlSession#selectList(………………)->
          ……………………
      public <E> List<E> query(…………) throws SQLException {
        BoundSql boundSql = ms.getBoundSql(parameterObject);
        //生成缓存key
        CacheKey key = createCacheKey(ms, parameterObject, rowBounds, boundSql);
        return query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
      } 
    

    继续执行下面方法生成缓存key,

    key规则=ms.getId()+rowBounds.getOffset()+rowBounds.getLimit()+boundSql.getSql()

    org.apache.ibatis.executor.BaseExecutor#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());
       ………………
        return cacheKey;
      }
    
    
       继续执行以下方法
        org.apache.ibatis.executor.CachingExecutor#query(…………){
        //从缓存执行语句中获取
        Cache cache = ms.getCache();
          //获取到缓存从缓存获取
        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.query(………………);
              tcm.putObject(cache, key, list); // issue #578 and #116
            }
            return list;
          }
        }
        //缓存中没有 执行以下方法 该方法真正执行sql 并打印语句
        return delegate.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
      }  
      
      
    

    继续执行以下方法 查询数据

    
      org.apache.ibatis.executor.BaseExecutor#query(……………………) {
      …………………………
        List<E> list;
        try {
          queryStack++;
          //从缓存中获取
          list = resultHandler == null ? (List<E>) localCache.getObject(key) : null;
          if (list != null) {
            handleLocallyCachedOutputParameters(ms, key, parameter, boundSql);
          } else {
            //为空执行sql
            list = queryFromDatabase(ms, parameter, rowBounds, resultHandler, key, boundSql);
          }
        } finally {
          queryStack--;
        }
        ……………………
          //如果一级缓存设置STATEMENT 清除缓存每次都是重新执行查询
          if (configuration.getLocalCacheScope() == LocalCacheScope.STATEMENT) {
            // issue #482
            clearLocalCache();
          }
        }
        return list;
      }
       
    

    继续执行以下方法:把数据保存在缓存中

    org.apache.ibatis.executor.BaseExecutor#queryFromDatabase(………………){
        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;
      }
    

    3:session提交可以清除缓存

    commit-result.png

    执行commit,执行相同参数查询,打印出两条日志,从代码里面查看是把缓存清空,代码比较简单,可以自行跟踪。

    4:查看一级缓存默认配置源码

    org.apache.ibatis.builder.xml.XMLConfigBuilder#settingsElement(…………){
        …………………………
         //一级缓存默认是true 开启
        configuration.setCacheEnabled(booleanValueOf(props.getProperty("cacheEnabled"), true));
        ………………
    //一级缓存默认是session级别     
          configuration.setLocalCacheScope(LocalCacheScope.valueOf(props.getProperty("localCacheScope", "SESSION")));
         ………………
      }
    
    

    5:总结

    mybatis有一级和二级缓存,一级缓存默认开启,二级缓存默认关闭不推荐使用,一级缓存默认开启会话级别,也可以设置STATEMENT级别,如果设置STATEMENT同一个session每次查询不会缓存,都是直接执行sql

    源码地址

    相关文章

      网友评论

          本文标题:mybatis源码之二 缓存

          本文链接:https://www.haomeiwen.com/subject/qispjctx.html