美文网首页
Cache接口的设计以及CacheKey的定义

Cache接口的设计以及CacheKey的定义

作者: 云芈山人 | 来源:发表于2021-06-27 07:21 被阅读0次

    Mybatis 定义了org.apache.ibatis.cache.Cache接口作为Cache的SPI(Service Provider Interface),Mybatis所有的内部缓存都需要去实现这个接口。MyBatis定义了PerpetualCache实现类实现了Cache接口。事实上,为SqlSession对象创建的Executor内部维护的Cache类型实例对象就是PerpetualCache子类创建的。

    • 一级缓存只涉及到PerpetualCache子类。
    • Cache的其它实现如下图:


      Cache装饰器.png

    如何确定Cache中key?

    Cache最核心的实现就是HashMap,将查询使用的特征值作为key,将查询到的结果作为value,存储到HashMap中。

    • 问题
      如何确定Cache中key?也就是怎样来确定此次查询的特征值,也可以说是怎么样判断两次查询是完全相同的查询?

    • 分析
      要确定两次查询是同一个查询,就需要满足以上四种特征:

      1. 传入的statementId
        对于MyBatis来说,要使用它,必须要有一个statementId,它代表着你会执行怎样的Sql。
      2. 查询中要求查询结果集中的结果范围
        MyBatis自身的分页通过RowBounds来实现,它通过rowBounds.offset和rowBounds.limit来过滤查询出来的结果集。这种分页功能是查询结果的再过滤,并非是数据库的物理分页。
      3. 这次查询所产生的最终要传递给JDBC java.sql.Preparedstatement的sql语句字符串(boundSql.getSql() )
      4. 传递给java.sql.Statement要设置的参数值
        第3和第4条的本质要求就是:调用JDBC时,传入的SQL语句要一模一样,传递给JDBC的参数值也要一模一样。
    • 结论
      综上可得出,CacheKey的组合应如下:
      statementId + rowBounds + 传递给JDBC的sql + 传递给JDBC的参数

    • CacheKey的创建
      对于每次的查询请求,Executor会根据传递的参数信息以及动态生产的SQL语句,按上述条件根据一定的计算规则,创建对应的CacheKey对象。
      1 目的
      CacheKey作为key去缓存Cache中查找缓存结果;
      如果查找缓存命中失败,可以此CacheKey为key,数据库查询的结果为value,组成键值对,存入Cache缓存中。

      1. 代码
        构建放在了Executor接口的实现类BaseExecutor中:
    @Override
      public CacheKey createCacheKey(MappedStatement ms, Object parameterObject, RowBounds rowBounds, BoundSql boundSql) {
        if (closed) {
          throw new ExecutorException("Executor was closed.");
        }
        CacheKey cacheKey = new CacheKey();
        //1.statementId
        cacheKey.update(ms.getId());
        //2. rowBounds.offset
        cacheKey.update(rowBounds.getOffset());
        //3. rowBounds.limit
        cacheKey.update(rowBounds.getLimit());
        //4. SQL语句
        cacheKey.update(boundSql.getSql());
        //5. 将每一个要传递给JDBC的参数值也更新到CacheKey中
        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;
      }
    
    • CacheKey的hashcode生成算法
      Cache接口的实现,本质是使用HashMap<k,v>,CacheKey作为key值,HashMap通过key值的hashcode来组织和存储,也即是构建CacheKey实际上就是构造其hashCode的过程。

    CacheKey的核心hashcode生成算法

    public void update(Object object) {
        if (object != null && object.getClass().isArray()) {
          int length = Array.getLength(object);
          for (int i = 0; i < length; i++) {
            Object element = Array.get(object, i);
            doUpdate(element);
          }
        } else {
          doUpdate(object);
        }
      }
     
      private void doUpdate(Object object) {
        
        //1. 得到对象的hashcode;  
        int baseHashCode = object == null ? 1 : object.hashCode();
        //对象计数递增
        count++;
        checksum += baseHashCode;
        //2. 对象的hashcode 扩大count倍
        baseHashCode *= count;
        //3. hashCode * 拓展因子(默认37)+拓展扩大后的对象hashCode值
        hashcode = multiplier * hashcode + baseHashCode;
        updateList.add(object);
      }
    

    相关文章

      网友评论

          本文标题:Cache接口的设计以及CacheKey的定义

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