Mybatis(九):源码分析-本地缓存和二级缓存

CachingExcuxtor:

  • 装饰器设计模式:缓存装饰器类,对真正的执行器Exexutor进行装饰实现缓存作用
  • 属性Executor delegate:委托对象,存储真正操作数据库的Executor执行器对象;
  • 属性TransactionalCacheManager:管理CachingExecutor中使用二级缓存的对象TransactionalCache;
  • TransactionalCache:实现Cache类,装饰了二级缓存对象;事务中需要进行二级缓存的各类数据,缓存数据在事务提交后才会真正存入二级缓存中;或事务回退后被丢弃,对缓存没有影响。
  • 大部分更新、关闭等方法都是调用委托对象对应的方法delegate.xxx();重点装饰query()方法来使用二级缓存查询数据

Mybatis一级缓存(即本地缓存 local cache):

  • 基于org.apache.ibatis.cache.impl.PerpetualCache类实现,内也是使用Map<Object, Object> cache = new HashMap<>()本地HashMap缓存,存储的作用域为Session会话,会缓存一个会话中执行的所有查询,通过SqlSession.openSesion()开启一个会话就会有一个一级缓存。
  • mybatis-config.xml全局配置:默认全局开启,可通过settings标签显示设置localCacheScope,<setting name="localCacheScope" value="SESSION | STATEMENT">
  • 本地缓存实现类PerpetualCache实现了接口Cache;
  • 本地缓存的使用源码可以查看BaseExecutor.query()方法 localCache.getObject(key)
  • Scope设置为Statement将不使用缓存:BaseExecutor.query()方法 configuration.getLocalCacheScope() == LocalCacheScope.STATEMENT会clearLocalCache();
  • 本地缓存的生命周期和会话的周期是一样的:因为DefaultSqlSession.close()方法调用了executor.close(),BaseExecutor里的close也调用了clearLocalCache方法清除本地缓存。

Mybatis二级缓存(second level cache):

  • mybatis-config.xml全局开关:默认开启全局二级缓存开关,通过settings标签,可以显示的设置<setting name="cacheEnabled" value="true"/>,全局性的开启或关闭所有映射器Mapper配置文件中已配置的任何缓存
  • Mapper.xml配置使用二级缓存:二级缓存是基于namespace级别的,所以一个命名空间对应一个二级缓存,如果需要在当前Mapper.xml中配置<cache />标签,也可以通过标签的属性自定义一些缓存控制参数,或者通过修改默认属性type="org.apache.ibatis.cache.impl.PerpetualCache"来指定自己的类实现Cache接口来自定义缓存。
  • Select标签:默认属性useCache="true" flushCahce="false",userCache表示是否使用二级缓存(false为不使用,每次直接从数据库中获取), flushCahce为语句被调用时,是否清空一级缓存和二级缓存;
  • insert、update、delete更新标签语句:只有默认属性flushCahce="true",表示语句被调用时都会去清空一级缓存和二级缓存,如果不执行刷新缓存会出现脏读;
  • 二级缓存的使用源码可以查看CachingExecutor.query()方法 tcm.getObject(cache, key)
  • 所有数据都会先放在一级缓存(本地缓存)中,只有当绘画提交或关闭时才会提交到二级缓存中
  • 二级缓存保证查询的时候必须唯一,往往对象的key可以使用String对象,而二级缓存通过生成一个复杂的CacheKey来做缓存项,key的构成命名空间、方法名、翻页偏移量、SQL语句、参数、数据源环境等,由特定的hashCode计算方法和内部属性来确定是否时同一个Cachekey。

二级缓存的流程分析:

  • 在Mapper.xml标签中配置<cache />
  • XMLMapperBuilder.configurationElement()方法便利解析各个元素节点;
  • cacheElement(context.evalNode("cache"));解析cache配置,获取各个属性,调用builderAssistant.useNewCache();
  • MapperBuilderAssistant.useNewCache()方法会实例化Cache当前Mapper.xml的二级缓存对象,并通过configuration.addCache(cache);方法添加到全局配置信息的Map<String, Cache> caches中来存储二级缓存对象。
  • CachingExecutor.query()方法执行查询操作时,通过Cache cache = ms.getCache()获取二级缓存对象
  • 通过tcm.getObject(cache, key)获取二级缓存对象cache,指定的CacheKey的结果。

默认开启缓存时,SQL执行器会使用CachingExcuxtor缓存装饰类去装饰真正的执行器(BatchExecutor、ReuseExecutor、SimpleExecutor)。所以执行executor.query()会经过CachingExcuxtor装饰类的二级缓存逻辑

// CachingExecutor 源码
package org.apache.ibatis.executor;

public class CachingExecutor implements Executor {
  // 委托对象,存储真正操作数据库的Executor执行器对象
  private final Executor delegate;
  // TransactionalCacheManager 管理CachingExecutor中使用二级缓存的对象TransactionalCache
  // TransactionalCache 实现Cache类,封装了二级缓存对象
  private final TransactionalCacheManager tcm = new TransactionalCacheManager();

  public CachingExecutor(Executor delegate) {
    // 设置委托对象
    this.delegate = delegate;
    // SQL执行器中添加设置缓存装饰器
    delegate.setExecutorWrapper(this);
  }

  @Override
  public Transaction getTransaction() {
    // 获取委托对象的事务对象
    return delegate.getTransaction();
  }
  
    @Override
    public void close(boolean forceRollback) {
      try {
        // issues #499, #524 and #573
        // tcm事务缓存管理对象关闭
        if (forceRollback) {
          tcm.rollback();
        } else {
          tcm.commit();
        }
      } finally {
        // 委托对象关闭
        delegate.close(forceRollback);
      }
    }
    
    @Override
    public boolean isClosed() {
      return delegate.isClosed();
    }
    
    @Override
    public int update(MappedStatement ms, Object parameterObject) throws SQLException {
      // 更新前清理缓存
      flushCacheIfRequired(ms);
      // 调用真正的执行器的update方法更新数据
      return delegate.update(ms, parameterObject);
    }
    
    @Override
    public <E> Cursor<E> queryCursor(MappedStatement ms, Object parameter, RowBounds rowBounds) throws SQLException {
      // 更新前清理缓存
      flushCacheIfRequired(ms);
      // 调用真正的执行器的queryCursor方法查询游标
      return delegate.queryCursor(ms, parameter, rowBounds);
    }
    
    @Override
    public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {
      // 获取BoundSql对象
      BoundSql boundSql = ms.getBoundSql(parameterObject);
      // 创建查询语句对应的Cachekey对象
      CacheKey key = createCacheKey(ms, parameterObject, rowBounds, boundSql);
      return query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
    }
    
    @Override
    public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql)
        throws SQLException {
      // 检查是否开启了二级缓存,即是否存在对应Mapper.xml的二级缓存对象
      Cache cache = ms.getCache();
      if (cache != null) {
        // 判断是否清空二级缓存
        // 通过Mapper.xml的SQL语句标签上的flushCache="true" || 或!select标签
        flushCacheIfRequired(ms);
        // isUseCache() = (Mapper.xml的SQL语句标签上的useCache="true" || select标签) ;true为使用缓存
        if (ms.isUseCache() && resultHandler == null) {
          // 存储过程语句时校验参数是否包含输入类型的参数
          ensureNoOutParams(ms, boundSql);
          @SuppressWarnings("unchecked")
          // 通过二级缓存对象Cache,取出对应CacheKey的缓存数据
          List<E> list = (List<E>) tcm.getObject(cache, key);
          if (list == null) {
            // 二级缓存中没有查到结果则使用委托对象来执行SQL查询
            list = delegate.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
            // 将查询结果缓存
            tcm.putObject(cache, key, list); // issue #578 and #116
          }
          return list;
        }
      }
      // 没开启二级缓存直接使用委托对象来执行查询
      return delegate.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
    }
    
    @Override
    public List<BatchResult> flushStatements() throws SQLException {
      return delegate.flushStatements();
    }
    
    @Override
    public void commit(boolean required) throws SQLException {
      delegate.commit(required);
      tcm.commit();
    }
    
    @Override
    public void rollback(boolean required) throws SQLException {
      try {
        delegate.rollback(required);
      } finally {
        if (required) {
          tcm.rollback();
        }
      }
    }
    
    private void ensureNoOutParams(MappedStatement ms, BoundSql boundSql) {
      if (ms.getStatementType() == StatementType.CALLABLE) {
        for (ParameterMapping parameterMapping : boundSql.getParameterMappings()) {
          if (parameterMapping.getMode() != ParameterMode.IN) {
            throw new ExecutorException("Caching stored procedures with OUT params is not supported.  Please configure useCache=false in " + ms.getId() + " statement.");
          }
        }
      }
    }
    
    @Override
    public CacheKey createCacheKey(MappedStatement ms, Object parameterObject, RowBounds rowBounds, BoundSql boundSql) {
      return delegate.createCacheKey(ms, parameterObject, rowBounds, boundSql);
    }
    
    @Override
    public boolean isCached(MappedStatement ms, CacheKey key) {
      return delegate.isCached(ms, key);
    }
    
    @Override
    public void deferLoad(MappedStatement ms, MetaObject resultObject, String property, CacheKey key, Class<?> targetType) {
      delegate.deferLoad(ms, resultObject, property, key, targetType);
    }
    
    @Override
    public void clearLocalCache() {
      delegate.clearLocalCache();
    }
    
    private void flushCacheIfRequired(MappedStatement ms) {
      Cache cache = ms.getCache();
      if (cache != null && ms.isFlushCacheRequired()) {
        tcm.clear(cache);
      }
    }
    
    @Override
    public void setExecutorWrapper(Executor executor) {
      throw new UnsupportedOperationException("This method should not be called");
    }
  
}

当没有拿到缓存时,会调用委托对象的查询方法delegate.query();拿简单执行器SimpleExecutor的executor.query()来分析,SimpleExecutor继承于BaseExecutor,query方法的实现在BaseExecutor类中。在BaseExecutor也进行一级缓存拿取的逻辑,如果没有缓存则才会从数据库中查询list = queryFromDatabase(ms, parameter, rowBounds, resultHandler, key, boundSql);

// BaseExecutor 源码
package org.apache.ibatis.executor;

public abstract class BaseExecutor implements Executor {
    
    @Override
    public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {
      // 从MappedStatement对象中根据参数获取BoundSql对象
      BoundSql boundSql = ms.getBoundSql(parameter);
      // 生成缓存的唯一key
      CacheKey key = createCacheKey(ms, parameter, rowBounds, boundSql);
      return query(ms, parameter, rowBounds, resultHandler, key, boundSql);
    }
    
    @SuppressWarnings("unchecked")
    @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.");
      }
      // 查询调用栈为0,且Mapper.xml的SQL语句标签上的flushCache="true" || !select标签
      // 则清空一级缓存,清空输出参数缓存
      if (queryStack == 0 && ms.isFlushCacheRequired()) {
        clearLocalCache();
      }
      List<E> list;
      try {
        // 查询调用栈++
        queryStack++;
        // 结果处理器为null时,尝试从一级缓存获取已缓存结果
        list = resultHandler == null ? (List<E>) localCache.getObject(key) : null;
        if (list != null) {
          // 如果查到localCache缓存,处理输出参数缓存
          handleLocallyCachedOutputParameters(ms, key, parameter, boundSql);
        } else {
          // 没有缓存结果,则从数据库查询结果
          list = queryFromDatabase(ms, parameter, rowBounds, resultHandler, key, boundSql);
        }
      } finally {
        queryStack--;
      }
      if (queryStack == 0) {
        // 延迟加载集合,遍历加载数据到list中
        for (DeferredLoad deferredLoad : deferredLoads) {
          // 加载延迟加载类
          deferredLoad.load();
        }
        // issue #601
        // 延迟加载集合清空
        deferredLoads.clear();
        // "localCacheScope"一级缓存存储作用域 value="STATEMENT"
        if (configuration.getLocalCacheScope() == LocalCacheScope.STATEMENT) {
          // issue #482
          // 则清空一级缓存,不做存储
          clearLocalCache();
        }
      }
      return list;
    }
    
    // 抽象的方法,需要具体的执行器来实现逻辑,模版方法模式
    protected abstract <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql)
    throws SQLException;
    
    // 从数据中查询数据
    private <E> List<E> queryFromDatabase(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {
      List<E> list;
      // 一级缓存,使用key添加一个一级缓存占位符
      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;
    }
    
    // 创建缓存项key
    @Override
    public CacheKey createCacheKey(MappedStatement ms, Object parameterObject, RowBounds rowBounds, BoundSql boundSql) {
      // 如果执行器关闭则报错
      if (closed) {
        throw new ExecutorException("Executor was closed.");
      }
      CacheKey cacheKey = new CacheKey();
      // MappedStatement的id,即namespace+id
      cacheKey.update(ms.getId());
      // sql分页参数的偏移量
      cacheKey.update(rowBounds.getOffset());
      // sql分页参数的限制条数
      cacheKey.update(rowBounds.getLimit());
      // SQL语句本身
      cacheKey.update(boundSql.getSql());
      // 传给JDBC的参数
      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())) {
            // 如果存在parameterObject对应类型处理器,value即为parameterObject
            value = parameterObject;
          } else {
            // 从元对象中获取propertyName的值
            MetaObject metaObject = configuration.newMetaObject(parameterObject);
            value = metaObject.getValue(propertyName);
          }
          // 添加参数值
          cacheKey.update(value);
        }
      }
      if (configuration.getEnvironment() != null) {
        // issue #176
        // 获取环境id,当配置多个环境时,需要通过环境id区分
        cacheKey.update(configuration.getEnvironment().getId());
      }
      return cacheKey;
    }
    
}

TransactionalCache(二级缓存的事务缓存类):

// TransactionalCache 源码
package org.apache.ibatis.cache.decorators;

// 事务中需要进行二级缓存的各类数据
// 缓存数据在事务提交后才会真正存入二级缓存中;或事务回退后被丢弃,对缓存没有影响
public class TransactionalCache implements Cache {
  private static final Log log = LogFactory.getLog(TransactionalCache.class);

   // 二级缓存缓存委托对象,真正存放缓存数据的二级缓存对象
  private final Cache delegate;
  // 是否在事务提交时清空二级缓存标识,
  // 为true时也不可以查询当前TransactionalCache的缓存数据
  private boolean clearOnCommit;
  // 需要在事务提交后存储到二级缓存中的数据Map,缓存项key和缓存值value的Map
  // 在事务提交时,会将其中的数据添加到二级缓存中
  private final Map<Object, Object> entriesToAddOnCommit;
  // 未命中的缓存key记录,事务提交时,也会放入二级缓存中(key,null)
  private final Set<Object> entriesMissedInCache;

  public TransactionalCache(Cache delegate) {
    this.delegate = delegate;
    this.clearOnCommit = false;
    this.entriesToAddOnCommit = new HashMap<>();
    this.entriesMissedInCache = new HashSet<>();
  }

  @Override
  public String getId() {
    return delegate.getId();
  }

  @Override
  public int getSize() {
    return delegate.getSize();
  }

  // 查询二级缓存的缓存数据
  @Override
  public Object getObject(Object key) {
    // issue #116
    // 直接查询缓存结果
    Object object = delegate.getObject(key);
    if (object == null) {
      // 没查到缓存数据时,将CacheKey添加到未命中的缓存key记录中
      entriesMissedInCache.add(key);
    }
    // issue #146
    // 为true则不返回缓存数据
    if (clearOnCommit) {
      return null;
    } else {
      return object;
    }
  }

  @Override
  public void putObject(Object key, Object object) {
    // 记录需要放入缓存的key,value
    entriesToAddOnCommit.put(key, object);
  }

  @Override
  public Object removeObject(Object key) {
    return null;
  }

  @Override
  // 清空
  public void clear() {
    clearOnCommit = true;
   // 清空entriesToAddOnCommit记录的缓存
    entriesToAddOnCommit.clear();
  }
  // 事务提交
  public void commit() {
    if (clearOnCommit) {
      // 清空二级缓存
      delegate.clear();
    }
    // 将entriesToAddOnCommit记录的缓存key和value刷新到二级缓存中
    flushPendingEntries();
    reset();
  }

  // 事务回滚
  public void rollback() {
    unlockMissedEntries();
    reset();
  }
  // 重置标识
  // 清空entriesToAddOnCommit集合和entriesMissedlnCache集合
  private void reset() {
    clearOnCommit = false;
    entriesToAddOnCommit.clear();
    entriesMissedInCache.clear();
  }

  private void flushPendingEntries() {
    // 将entriesToAddOnCommit记录的缓存key和value刷新到二级缓存中
    for (Map.Entry<Object, Object> entry : entriesToAddOnCommit.entrySet()) {
      delegate.putObject(entry.getKey(), entry.getValue());
    }
    // 将entriesMissedlnCache集合中记录的缓存项刷新到二级缓存中
    for (Object entry : entriesMissedInCache) {
      if (!entriesToAddOnCommit.containsKey(entry)) {
        delegate.putObject(entry, null);
      }
    }
  }
  
  // 事务回滚时调用,解锁丢失的缓存数据
  // entriesMissedlnCache集合中记录的缓存项从二级缓存中删除
  private void unlockMissedEntries() {
    for (Object entry : entriesMissedInCache) {
      try {
        delegate.removeObject(entry);
      } catch (Exception e) {
        log.warn("Unexpected exception while notifying a rollback to the cache adapter. "
            + "Consider upgrading your cache adapter to the latest version. Cause: " + e);
      }
    }
  }

}
// TransactionalCacheManager 源码
package org.apache.ibatis.cache;

// 管理 CachingExecutor中使用的二级缓存对象TransactionalCache
public class TransactionalCacheManager {
  // 对应的二级缓存对象
  private final Map<Cache, TransactionalCache> transactionalCaches = new HashMap<>();

  public void clear(Cache cache) {
    // 清空二级缓存
    getTransactionalCache(cache).clear();
  }
  // 获取二级缓存对象中指定key的缓存结果
  public Object getObject(Cache cache, CacheKey key) {
    return getTransactionalCache(cache).getObject(key);
  }
  // 添加指定Mapper的Cache二级缓存对象,缓存key和value
  public void putObject(Cache cache, CacheKey key, Object value) {
    getTransactionalCache(cache).putObject(key, value);
  }
  // 事务提交
  public void commit() {
    for (TransactionalCache txCache : transactionalCaches.values()) {
      txCache.commit();
    }
  }
  // 回滚
  public void rollback() {
    for (TransactionalCache txCache : transactionalCaches.values()) {
      txCache.rollback();
    }
  }
  // 获取二级缓存对象
  private TransactionalCache getTransactionalCache(Cache cache) {
    return MapUtil.computeIfAbsent(transactionalCaches, cache, TransactionalCache::new);
  }

}

Mybatis缓存的Key的设计:通过自定义的CacheKey类

// CacheKey 源码
package org.apache.ibatis.cache;

// 缓存项key,来确定一个缓存项
public class CacheKey implements Cloneable, Serializable {

  private static final long serialVersionUID = 1146682552656046210L;
  
  // 定义一个空的缓存项,空缓存项不能进行更新
  public static final CacheKey NULL_CACHE_KEY = new CacheKey() {
    @Override
    public void update(Object object) {
      throw new CacheException("Not allowed to update a null cache key instance.");
    }

    @Override
    public void updateAll(Object[] objects) {
      throw new CacheException("Not allowed to update a null cache key instance.");
    }
  };

  // 乘子,用来作为hashCode计算的乘数
  // 通常使用的备选乘数:31, 33, 37, 39 和 41 
  private static final int DEFAULT_MULTIPLIER = 37;
  // hashCode值,默认17,hashcode初始质子数17
  // 如果使用较小质子数,那么乘积范围较小,容易造成哈希值的冲突
  // 如果使用较大质子数,得到的hashCode值会超过int范围
  private static final int DEFAULT_HASHCODE = 17;
  
  // 乘子,用来作为hashCode计算的乘数
  private final int multiplier;
  // hashCode值
  private int hashcode;
  // 校验和,hash值的和
  private long checksum;
  // pdateList的中元素个数
  private int count;
  // 8/21/2017 - Sonarlint flags this as needing to be marked transient. While true if content is not serializable, this
  // is not always true and thus should not be marked transient.
  // 存储namepace.id 、offerset、limit、sql、params等
  private List<Object> updateList;

  // 构造初始化
  public CacheKey() {
    this.hashcode = DEFAULT_HASHCODE;
    this.multiplier = DEFAULT_MULTIPLIER;
    this.count = 0;
    this.updateList = new ArrayList<>();
  }

  public CacheKey(Object[] objects) {
    this();
    updateAll(objects);
  }

  public int getUpdateCount() {
    return updateList.size();
  }

  public void update(Object object) {
    // 获取object的hash值
    int baseHashCode = object == null ? 1 : ArrayUtil.hashCode(object);
    // 更新count、checksum以及hashcode的值
    count++;
    checksum += baseHashCode;
    baseHashCode *= count;
    // hashcode更新的方法
    // newHashcode = oldHashcode * 37 + baseHashCode
    hashcode = multiplier * hashcode + baseHashCode;
    // 将对象添加到updateList中
    updateList.add(object);
  }
  
  // 更新数组对象
  public void updateAll(Object[] objects) {
    for (Object o : objects) {
      // 调用单个更新方法
      update(o);
    }
  }

  // 重载对象是否相同判断方法
  @Override
  public boolean equals(Object object) {
    // 判断是否为同一个对象
    if (this == object) {
      return true;
    }
    // 对象不是CacheKey的实例直接返回false
    if (!(object instanceof CacheKey)) {
      return false;
    }
    // 对象转为CacheKey类型
    final CacheKey cacheKey = (CacheKey) object;
    // 判断hashcode是否相同
    if (hashcode != cacheKey.hashcode) {
      return false;
    }
     // 判断checksum是否相同
    if (checksum != cacheKey.checksum) {
      return false;
    }
    // 判断updateList数量是否相同
    if (count != cacheKey.count) {
      return false;
    }
    // 以上都相同,通过遍历updateList元素中是否相同
    for (int i = 0; i < updateList.size(); i++) {
      Object thisObject = updateList.get(i);
      Object thatObject = cacheKey.updateList.get(i);
      if (!ArrayUtil.equals(thisObject, thatObject)) {
        return false;
      }
    }
    return true;
  }

  @Override
  public int hashCode() {
    return hashcode;
  }

  @Override
  public String toString() {
    StringJoiner returnValue = new StringJoiner(":");
    returnValue.add(String.valueOf(hashcode));
    returnValue.add(String.valueOf(checksum));
    updateList.stream().map(ArrayUtil::toString).forEach(returnValue::add);
    return returnValue.toString();
  }

  @Override
  public CacheKey clone() throws CloneNotSupportedException {
    // 克隆CacheKey对象
    CacheKey clonedCacheKey = (CacheKey) super.clone();
    // 复制数组重新赋值
    clonedCacheKey.updateList = new ArrayList<>(updateList);
    return clonedCacheKey;
  }

}

Mybatis缓存的设计,通过Cache接口实现类:

  • PerpetualCache:永久缓存,只有这唯一一个基础实现(org.apache.ibatis.cache.impl),其他实现类全都是装饰模式(org.apache.ibatis.cache.decorators)持有另一个缓存对象;
  • PerpetualCache里面就是维护着一个本地的HashMap,所以不支持多线程,因为没能实现接口的锁功能,而且内部的map不是并发的map;
  • FifoCache(先进先出缓存策略的缓存装饰类)
  • LruCache(最近最少使用的缓存策略的缓存装饰类)
  • SoftCache(软引用回收策略缓存装饰类)
  • WeakCache(弱引用回收策略缓存装饰类)
  • LoggingCache(日志功能装饰类,用于记录缓存的命中率)
  • ScheduledCache(定时清空Cache类)
  • SerializedCache(序列化功能,将值序列化后存到缓存中)
  • SynchronizedCache(同步的缓存Cache类)
  • TransactionalCache(二级缓存的事务缓存类)
// PerpetualCache 源码
package org.apache.ibatis.cache.impl;


public class PerpetualCache implements Cache {
  // 缓冲对象的唯一标识,namespace.id
  private final String id;
  // 通过HashMap存储
  private final Map<Object, Object> cache = new HashMap<>();

  public PerpetualCache(String id) {
    this.id = id;
  }

  @Override
  public String getId() {
    return id;
  }

  @Override
  public int getSize() {
    return cache.size();
  }

  @Override
  public void putObject(Object key, Object value) {
    cache.put(key, value);
  }

  @Override
  public Object getObject(Object key) {
    return cache.get(key);
  }

  @Override
  public Object removeObject(Object key) {
    return cache.remove(key);
  }

  @Override
  public void clear() {
    cache.clear();
  }

  @Override
  public boolean equals(Object o) {
    // id不存在报错
    if (getId() == null) {
      throw new CacheException("Cache instances require an ID.");
    }
    // 对象判断是否相等,即存储地址判断
    if (this == o) {
      return true;
    }
    // 是否是子类
    if (!(o instanceof Cache)) {
      return false;
    }

    Cache otherCache = (Cache) o;
    // 通过判断ID相等
    return getId().equals(otherCache.getId());
  }

  @Override
  public int hashCode() {
    if (getId() == null) {
      throw new CacheException("Cache instances require an ID.");
    }
    // 用属性ID的hashCode作为对象的hashCode
    return getId().hashCode();
  }
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值