侧边栏壁纸
博主头像
落叶人生博主等级

走进秋风,寻找秋天的落叶

  • 累计撰写 130562 篇文章
  • 累计创建 28 个标签
  • 累计收到 9 条评论
标签搜索

目 录CONTENT

文章目录

MyBatis源码解析(四) Cache模块

2022-07-16 星期六 / 0 评论 / 0 点赞 / 32 阅读 / 28450 字

MyBatis的二级缓存MyBatis的缓存分为一级缓存和二级缓存,一级缓存是 SqlSession 级别的缓存,二级缓存是mapper级别的缓存。但是这篇博客主要是介绍mybaits中缓存接口和缓存

MyBatis的二级缓存

MyBatis的缓存分为一级缓存和二级缓存,一级缓存是 SqlSession 级别的缓存,二级缓存是mapper级别的缓存。但是这篇博客主要是介绍mybaits中缓存接口和缓存键接口,以及一些缓存实现。

之前写过一篇博客简单介绍了一下Hibernate的两级缓存。

链接: https://blog.csdn.net/Letmetell_you/article/details/80876767

Cache接口

源码位置:org.apache.ibatis.cache.Cache

缓存容器接口,自定义操作方法,其他缓存实现类需要实现这个接口。

UML类图

上图中列出来的就是Cache接口的实现类,实现不同的缓存功能。

接口的源码很简单,就是定义了一些增删改查缓存的方法,和一个获取容器中缓存数量和获得读写锁的方法。我精简了下注释,源码内容如下。Cache接口其实是一个缓存容器,有点类似于一个HashMap(有一些实现类就是使用HashMap来保存操作缓存数据的)。

public interface Cache {  /**   * 获取标识   */  String getId();  /**   * 添加指定的键   */  void putObject(Object key, Object value);  /**   * 获取指定的键的值   */  Object getObject(Object key);  /**   * 删除指定的键的值   */  Object removeObject(Object key);  /**   * 清空缓存   */  void clear();  /**   * 获取容器中缓存的数量   */  int getSize();  /**   * 获得读写锁   */  default ReadWriteLock getReadWriteLock() {    return null;  }}

PerpetualCache

源码位置:org.apache.ibatis.cache.impl.PerpetualCache

永不过期的缓存,使用HashMap来保存和操作数据,重写了 equals()hashCode() 方法,其他缓存操作都是直接调用的HashMap方法。

LoggingCache

源码位置:org.apache.ibatis.cache.decorators.LoggingCache

这是一个支持打印日志的 Cache 实现,代码也很简单,加了一些注释。

public class LoggingCache implements Cache {  /**   * mybaits log 对象   */  private final Log log;  /**   * 装饰的 Cache 对象   */  private final Cache delegate;  /**   * 统计请求缓存的次数   */  protected int requests = 0;  /**   * 命中缓存的次数   */  protected int hits = 0;  public LoggingCache(Cache delegate) {    this.delegate = delegate;    this.log = LogFactory.getLog(getId());  }  @Override  public String getId() {    return delegate.getId();  }  @Override  public int getSize() {    return delegate.getSize();  }  @Override  public void putObject(Object key, Object object) {    delegate.putObject(key, object);  }  @Override  public Object getObject(Object key) {    //请求次数加 1    requests++;    final Object value = delegate.getObject(key);    if (value != null) {      //命中缓存,命中次数加 1      hits++;    }    if (log.isDebugEnabled()) {      //打印该缓存命中次数      log.debug("Cache Hit Ratio [" + getId() + "]: " + getHitRatio());    }    return value;  }  @Override  public Object removeObject(Object key) {    return delegate.removeObject(key);  }  @Override  public void clear() {    delegate.clear();  }  @Override  public int hashCode() {    return delegate.hashCode();  }  @Override  public boolean equals(Object obj) {    return delegate.equals(obj);  }  /**   * 计算命中比例   * @return   */  private double getHitRatio() {    //算法: 命中次数 / 请求缓存次数    return (double) hits / (double) requests;  }}

BlockingCache

源码位置:org.apache.ibatis.cache.decorators.BlockingCache

阻塞的Cache实现类,这个实现不同的逻辑是在加锁上。当一个线程去获取缓存事,缓存不存在则会阻塞后续线程获取,当前线程则去添加缓存值,避免后续线程重复添加缓存。需要注意的是这个实现里 removeObject 方法并不是删除缓存值,而是移除锁。

public class BlockingCache implements Cache {  /**   * 阻塞等待超时时间   */  private long timeout;  /**   * 装饰的Cache对象   */  private final Cache delegate;  /**   * 缓存键 与  ReentrantLock 对象映射   */  private final ConcurrentHashMap<Object, ReentrantLock> locks;  public BlockingCache(Cache delegate) {    this.delegate = delegate;    this.locks = new ConcurrentHashMap<>();  }  @Override  public String getId() {    return delegate.getId();  }  @Override  public int getSize() {    return delegate.getSize();  }  @Override  public void putObject(Object key, Object value) {    try {      //添加缓存      delegate.putObject(key, value);    } finally {      //释放锁      releaseLock(key);    }  }  @Override  public Object getObject(Object key) {    //获得锁    acquireLock(key);    //获得缓存执    Object value = delegate.getObject(key);    if (value != null) {      //释放锁      releaseLock(key);    }    return value;  }  @Override  public Object removeObject(Object key) {    // despite of its name, this method is called only to release locks    //释放该键对应的锁    releaseLock(key);    return null;  }  @Override  public void clear() {    delegate.clear();  }  /**   * 获得 ReentrantLock 对象,如果不存在,则进行添加   * @param key   * @return   */  private ReentrantLock getLockForKey(Object key) {    return locks.computeIfAbsent(key, k -> new ReentrantLock());  }  private void acquireLock(Object key) {    //获得键对应的 ReentrantLock 对象    Lock lock = getLockForKey(key);    //获得锁,直到超时    if (timeout > 0) {      try {        boolean acquired = lock.tryLock(timeout, TimeUnit.MILLISECONDS);        if (!acquired) {          throw new CacheException("Couldn't get a lock in " + timeout + " for the key " +  key + " at the cache " + delegate.getId());        }      } catch (InterruptedException e) {        throw new CacheException("Got interrupted while trying to acquire lock for key " + key, e);      }    } else {      //释放锁      lock.lock();    }  }  private void releaseLock(Object key) {    //获得 ReentrantLock 对象    ReentrantLock lock = locks.get(key);    if (lock.isHeldByCurrentThread()) {      //如果当前线程持有锁,进行释放      lock.unlock();    }  }  public long getTimeout() {    return timeout;  }  public void setTimeout(long timeout) {    this.timeout = timeout;  }}

SynchronizedCache

源码位置:org.apache.ibatis.cache.decorators.SynchronizedCache

同步Cache实现,内部也是使用装饰的Cache来实现缓存操作。不过这个实现在 getSize putObject getObject removeObject clear 这几个方法上添加了 synchronized关键字。

SerializedCache

源码位置:org.apache.ibatis.cache.decorators.SerializedCache

支持序列化值,其实就是在添加缓存的时候对值进行序列化,获取值的时候反序列化。

public class SerializedCache implements Cache {  //装饰的 Cache  private final Cache delegate;  public SerializedCache(Cache delegate) {    this.delegate = delegate;  }  @Override  public String getId() {    return delegate.getId();  }  @Override  public int getSize() {    return delegate.getSize();  }  @Override  public void putObject(Object key, Object object) {    if (object == null || object instanceof Serializable) {        //存值进行序列化      delegate.putObject(key, serialize((Serializable) object));    } else {      throw new CacheException("SharedCache failed to make a copy of a non-serializable object: " + object);    }  }  @Override  public Object getObject(Object key) {    Object object = delegate.getObject(key);      //取值进行反序列化    return object == null ? null : deserialize((byte[]) object);  }  @Override  public Object removeObject(Object key) {    return delegate.removeObject(key);  }  @Override  public void clear() {    delegate.clear();  }  @Override  public int hashCode() {    return delegate.hashCode();  }  @Override  public boolean equals(Object obj) {    return delegate.equals(obj);  }  private byte[] serialize(Serializable value) {    try (ByteArrayOutputStream bos = new ByteArrayOutputStream();         ObjectOutputStream oos = new ObjectOutputStream(bos)) {      oos.writeObject(value);      oos.flush();      return bos.toByteArray();    } catch (Exception e) {      throw new CacheException("Error serializing object.  Cause: " + e, e);    }  }  private Serializable deserialize(byte[] value) {    Serializable result;    try (ByteArrayInputStream bis = new ByteArrayInputStream(value);         ObjectInputStream ois = new CustomObjectInputStream(bis)) {      result = (Serializable) ois.readObject();    } catch (Exception e) {      throw new CacheException("Error deserializing object.  Cause: " + e, e);    }    return result;  }  public static class CustomObjectInputStream extends ObjectInputStream {    public CustomObjectInputStream(InputStream in) throws IOException {      super(in);    }    @Override    protected Class<?> resolveClass(ObjectStreamClass desc) throws ClassNotFoundException {      return Resources.classForName(desc.getName());    }  }}

ScheduledCache

源码位置:org.apache.ibatis.cache.decorators.ScheduledCache

定时清空整个 Cache 的缓存,在每次操作缓存之前判断是否全部清空缓存

public class ScheduledCache implements Cache {  /**   * 装饰的 Cache 对象   */  private final Cache delegate;  /**   * 清空间隔,单位:毫秒   */  protected long clearInterval;  /**   * 最后清空时间,单位:毫秒   */  protected long lastClear;  public ScheduledCache(Cache delegate) {    this.delegate = delegate;    //默认清空间隔 一小时    this.clearInterval = 60 * 60 * 1000; // 1 hour    this.lastClear = System.currentTimeMillis();  }  public void setClearInterval(long clearInterval) {    this.clearInterval = clearInterval;  }  @Override  public String getId() {    return delegate.getId();  }  @Override  public int getSize() {    //判断是否需要全部清空    clearWhenStale();    return delegate.getSize();  }  @Override  public void putObject(Object key, Object object) {    //判断是否需要全部清空    clearWhenStale();    delegate.putObject(key, object);  }  @Override  public Object getObject(Object key) {    //判断是否需要全部清空    return clearWhenStale() ? null : delegate.getObject(key);  }  @Override  public Object removeObject(Object key) {    //判断是否需要全部清空    clearWhenStale();    return delegate.removeObject(key);  }  @Override  public void clear() {    //记录清空时间    lastClear = System.currentTimeMillis();    delegate.clear();  }  @Override  public int hashCode() {    return delegate.hashCode();  }  @Override  public boolean equals(Object obj) {    return delegate.equals(obj);  }  /**   * 判断是否需要全部清空   * @return   */  private boolean clearWhenStale() {    if (System.currentTimeMillis() - lastClear > clearInterval) {      //全部清空      clear();      return true;    }    return false;  }}

FifoCache

源码位置:org.apache.ibatis.cache.decorators.FifoCache

基于先进先出淘汰机制的Cache实现,此实现在删除缓存时并不会删除缓存key,所以旧的 key 也依旧会继续存在。

除此之外在添加key的时候该实现也不会去判断key是否已经存在,只会判断当前长度是否超过了队列上上限,所以重复添加就会在队列里存在多个相同的key,这个不能说是bug,只能说是允许重复的key,但是如果你在使用中发现同一个key拿到的缓存不止一个,可能就需要检查一下你使用的是不是 FifoCache 实现了。

public class FifoCache implements Cache {  /**   * 装饰的 Cache 对象   */  private final Cache delegate;  /**   * 双端队列,记录键值的添加   */  private final Deque<Object> keyList;  /**   * 队列上限   */  private int size;  public FifoCache(Cache delegate) {    this.delegate = delegate;    this.keyList = new LinkedList<>();    this.size = 1024;  }  @Override  public String getId() {    return delegate.getId();  }  @Override  public int getSize() {    return delegate.getSize();  }  public void setSize(int size) {    this.size = size;  }  @Override  public void putObject(Object key, Object value) {    //循环 keyList    cycleKeyList(key);    delegate.putObject(key, value);  }  @Override  public Object getObject(Object key) {    return delegate.getObject(key);  }  @Override  public Object removeObject(Object key) {    //删除缓存,未清空keyList中的数据    return delegate.removeObject(key);  }  @Override  public void clear() {    //清空缓存时,同时清空维护的 keyList    delegate.clear();    keyList.clear();  }  private void cycleKeyList(Object key) {    //添加到 keyList    keyList.addLast(key);    if (keyList.size() > size) {      //如果添加新 key 之后 ketList 的长度大于 队列上线,将队列的首位移除      Object oldestKey = keyList.removeFirst();      delegate.removeObject(oldestKey);    }  }}

LruCache

源码位置:org.apache.ibatis.cache.decorators.LruCache

此实现是基于最少使用的淘汰机制的 Cache 实现,简单说就是当添加缓存时发现已经达到上限的时候,淘汰掉最少使用的 key 以及对应的缓存。使用 LinkedHashMap 的淘汰机制,具体源码的解析请参考下面的源码。

public class LruCache implements Cache {  /**   * 装饰的 Cache 对象   */  private final Cache delegate;  /**   * 基于 LinkedHashMap 实现淘汰机制   */  private Map<Object, Object> keyMap;  /**   * 最老/最少被使用的键,即将被淘汰的 key   */  private Object eldestKey;  public LruCache(Cache delegate) {    this.delegate = delegate;    //初始化 keyMap 对象    setSize(1024);  }  @Override  public String getId() {    return delegate.getId();  }  @Override  public int getSize() {    return delegate.getSize();  }  /**   * 初始化keyMap,但是此方法的权限标识是 public ,也就是说可以通过这个方法来指定 keyMap的长度,默认是1024,可以根据自己的需求来变更   * @param size   */  public void setSize(final int size) {    //LinkedHasmp的一个构造函数,当参数 accessOrder 为 true 时,即会按照访问的顺序排序,最近访问的在最前,最早访问的在最后    keyMap = new LinkedHashMap<Object, Object>(size, .75F, true) {      private static final long serialVersionUID = 4267176411845948333L;      @Override      protected boolean removeEldestEntry(Map.Entry<Object, Object> eldest) {        //重写 LinkedHashMapLinkedHashMap 的删除元素方法,当满足一定条件的时候删除元素,LinkedHashMap中默认是不删除        //这里判断的条件就是 keyMap 长度大于初始化 keyMap 时给定的值,满足条件时将最少使用的 key 设置为待删除,        // 等下次添加新key的时候判断 eldestKey 参数不为空则对 eldestKey 进行移除        boolean tooBig = size() > size;        if (tooBig) {          eldestKey = eldest.getKey();        }        return tooBig;      }    };  }  @Override  public void putObject(Object key, Object value) {    delegate.putObject(key, value);    cycleKeyList(key);  }  @Override  public Object getObject(Object key) {    keyMap.get(key); //touch    return delegate.getObject(key);  }  @Override  public Object removeObject(Object key) {    return delegate.removeObject(key);  }  @Override  public void clear() {    delegate.clear();    keyMap.clear();  }  private void cycleKeyList(Object key) {    //添加 key 到keyMap中    keyMap.put(key, key);    //如果超过上限,则删除最少使用的可以    if (eldestKey != null) {      //移除 eldestKey      delegate.removeObject(eldestKey);      //置空      eldestKey = null;    }  }}

WeakCache

源码位置:org.apache.ibatis.cache.decorators.WeakCache

基于 java.lang.ref.WeakReference 的Cache实现类,主要是基于内部维护的一个强引用和mybatis基于 java.lang.ref.WeakReference 扩展的WeakEntry来实现缓存淘汰。在这个实现里,一样存在key多次添加会导致重复key的问题。

public class WeakCache implements Cache {  //强引用的键的队列  private final Deque<Object> hardLinksToAvoidGarbageCollection;  //被GC回收的 WeakEntry 集合,避免被 GC  private final ReferenceQueue<Object> queueOfGarbageCollectedEntries;  //装饰的 Cache 对象  private final Cache delegate;  //hardLinksToAvoidGarbageCollection 的大小  private int numberOfHardLinks;  public WeakCache(Cache delegate) {    this.delegate = delegate;    this.numberOfHardLinks = 256;    this.hardLinksToAvoidGarbageCollection = new LinkedList<>();    this.queueOfGarbageCollectedEntries = new ReferenceQueue<>();  }  @Override  public String getId() {    return delegate.getId();  }  @Override  public int getSize() {    //移除已经被 GC 回收的WeakEntry    removeGarbageCollectedItems();    return delegate.getSize();  }  public void setSize(int size) {    this.numberOfHardLinks = size;  }  @Override  public void putObject(Object key, Object value) {    //移除已经被 GC 回收的 WeakEntry    removeGarbageCollectedItems();    //添加缓存    delegate.putObject(key, new WeakEntry(key, value, queueOfGarbageCollectedEntries));  }  @Override  public Object getObject(Object key) {    Object result = null;    @SuppressWarnings("unchecked") // assumed delegate cache is totally managed by this cache    //获得值的 WeakReference 对象    WeakReference<Object> weakReference = (WeakReference<Object>) delegate.getObject(key);    if (weakReference != null) {      //获得值      result = weakReference.get();      if (result == null) {        //值为空,表示已经被GC回收,移除缓存        delegate.removeObject(key);      } else {        //非空,添加到 hardLinksToAvoidGarbageCollection 队列首部,未做key唯一性判断,所以存在重复添加的情况 ,避免被 GC        hardLinksToAvoidGarbageCollection.addFirst(result);        if (hardLinksToAvoidGarbageCollection.size() > numberOfHardLinks) {          //如果长度超出上限,则移除队列尾部的元素          hardLinksToAvoidGarbageCollection.removeLast();        }      }    }    return result;  }  @Override  public Object removeObject(Object key) {    //移除已经被 GC 回收的 WeakEntry    removeGarbageCollectedItems();    //移除缓存    return delegate.removeObject(key);  }  @Override  public void clear() {    //清空 hardLinksToAvoidGarbageCollection    hardLinksToAvoidGarbageCollection.clear();    //移除已经被 GC 回收的 WeakEntry    removeGarbageCollectedItems();    //清空缓存    delegate.clear();  }  /**   * 移除已经被 GC 回收的键   */  private void removeGarbageCollectedItems() {    WeakEntry sv;    while ((sv = (WeakEntry) queueOfGarbageCollectedEntries.poll()) != null) {      delegate.removeObject(sv.key);    }  }  /**   * 继承自 WeakReference ,增加缓存key属性,   */  private static class WeakEntry extends WeakReference<Object> {    //键    private final Object key;    private WeakEntry(Object key, Object value, ReferenceQueue<Object> garbageCollectionQueue) {      super(value, garbageCollectionQueue);      this.key = key;    }  }}

SoftCache

源码位置:org.apache.ibatis.cache.decorators.SoftCache

基于java.lang.ref.SoftReference 的Cache实现,SoftCache内部实现了SoftEntry,其他基本上与WeakCache基本上是差不多的,只是在一些操作中会对 hardLinksToAvoidGarbageCollection 加锁。

public class WeakCache implements Cache {  //强引用的键的队列  private final Deque<Object> hardLinksToAvoidGarbageCollection;  //被GC回收的 WeakEntry 集合,避免被 GC  private final ReferenceQueue<Object> queueOfGarbageCollectedEntries;  //装饰的 Cache 对象  private final Cache delegate;  //hardLinksToAvoidGarbageCollection 的大小  private int numberOfHardLinks;  public WeakCache(Cache delegate) {    this.delegate = delegate;    this.numberOfHardLinks = 256;    this.hardLinksToAvoidGarbageCollection = new LinkedList<>();    this.queueOfGarbageCollectedEntries = new ReferenceQueue<>();  }  @Override  public String getId() {    return delegate.getId();  }  @Override  public int getSize() {    //移除已经被 GC 回收的WeakEntry    removeGarbageCollectedItems();    return delegate.getSize();  }  public void setSize(int size) {    this.numberOfHardLinks = size;  }  @Override  public void putObject(Object key, Object value) {    //移除已经被 GC 回收的 WeakEntry    removeGarbageCollectedItems();    //添加缓存    delegate.putObject(key, new WeakEntry(key, value, queueOfGarbageCollectedEntries));  }  @Override  public Object getObject(Object key) {    Object result = null;    @SuppressWarnings("unchecked") // assumed delegate cache is totally managed by this cache    //获得值的 WeakReference 对象    WeakReference<Object> weakReference = (WeakReference<Object>) delegate.getObject(key);    if (weakReference != null) {      //获得值      result = weakReference.get();      if (result == null) {        //值为空,表示已经被GC回收,移除缓存        delegate.removeObject(key);      } else {        //非空,添加到 hardLinksToAvoidGarbageCollection 队列首部,未做key唯一性判断,所以存在重复添加的情况 ,避免被 GC        hardLinksToAvoidGarbageCollection.addFirst(result);        if (hardLinksToAvoidGarbageCollection.size() > numberOfHardLinks) {          //如果长度超出上限,则移除队列尾部的元素          hardLinksToAvoidGarbageCollection.removeLast();        }      }    }    return result;  }  @Override  public Object removeObject(Object key) {    //移除已经被 GC 回收的 WeakEntry    removeGarbageCollectedItems();    //移除缓存    return delegate.removeObject(key);  }  @Override  public void clear() {    //清空 hardLinksToAvoidGarbageCollection    hardLinksToAvoidGarbageCollection.clear();    //移除已经被 GC 回收的 WeakEntry    removeGarbageCollectedItems();    //清空缓存    delegate.clear();  }  /**   * 移除已经被 GC 回收的键   */  private void removeGarbageCollectedItems() {    WeakEntry sv;    while ((sv = (WeakEntry) queueOfGarbageCollectedEntries.poll()) != null) {      delegate.removeObject(sv.key);    }  }  /**   * 继承自 WeakReference ,增加缓存key属性,   */  private static class WeakEntry extends WeakReference<Object> {    //键    private final Object key;    private WeakEntry(Object key, Object value, ReferenceQueue<Object> garbageCollectionQueue) {      super(value, garbageCollectionQueue);      this.key = key;    }  }}

CachKey

源码位置:org.apache.ibatis.cache.CacheKey

mybaits中的缓存键,不只是单纯的String字符串,而是由多个对象组成,共同计算缓存键。CacheKey中封装了多个影响缓存的属性。

public class CacheKey implements Cloneable, Serializable {  private static final long serialVersionUID = 1146682552656046210L;  //单例 空缓存键  public static final CacheKey NULL_CACHE_KEY = new NullCacheKey();  //multiplier 的值  private static final int DEFAULT_MULTIPLYER = 37;  //hashcode 的值  private static final int DEFAULT_HASHCODE = 17;  //hashcode求值的系数  private final int multiplier;  //缓存键的hashcode  private int hashcode;  //校验和  private long checksum;  //updateList 的长度  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.  //计算 hashcode 的对象的集合  private List<Object> updateList;  public CacheKey() {    this.hashcode = DEFAULT_HASHCODE;    this.multiplier = DEFAULT_MULTIPLYER;    this.count = 0;    this.updateList = new ArrayList<>();  }  public CacheKey(Object[] objects) {    this();    //基于 objects ,更新相关属性    updateAll(objects);  }  public int getUpdateCount() {    return updateList.size();  }  public void update(Object object) {    //方法参数 object 的 hashcode    int baseHashCode = object == null ? 1 : ArrayUtil.hashCode(object);    count++;    //checksum 为 baseHashCode 求和    checksum += baseHashCode;    //计算hashcode值    baseHashCode *= count;    hashcode = multiplier * hashcode + baseHashCode;    //添加 object(缓存key) 到updateList中    updateList.add(object);  }  public void updateAll(Object[] objects) {    //遍历 objects 数组,调用 update 方法,更新相关属性    for (Object o : objects) {      update(o);    }  }  @Override  public boolean equals(Object object) {    if (this == object) {      return true;    }    if (!(object instanceof CacheKey)) {      return false;    }    final CacheKey cacheKey = (CacheKey) object;    if (hashcode != cacheKey.hashcode) {      return false;    }    if (checksum != cacheKey.checksum) {      return false;    }    if (count != cacheKey.count) {      return false;    }    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();    //创建 updateList 数组,避免原数组修改    clonedCacheKey.updateList = new ArrayList<>(updateList);    return clonedCacheKey;  }    }    

NullCacheKey

源码位置:org.apache.ibatis.cache.NullCacheKey

继承自 CacheKey ,空缓存键。

public final class NullCacheKey extends CacheKey {  private static final long serialVersionUID = 3704229911977019465L;  public NullCacheKey() {    super();  }  @Override  public void update(Object object) {    throw new CacheException("Not allowed to update a NullCacheKey instance.");  }  @Override  public void updateAll(Object[] objects) {    throw new CacheException("Not allowed to update a NullCacheKey instance.");  }}public final class NullCacheKey extends CacheKey {  private static final long serialVersionUID = 3704229911977019465L;  public NullCacheKey() {    super();  }  @Override  public void update(Object object) {    throw new CacheException("Not allowed to update a NullCacheKey instance.");  }  @Override  public void updateAll(Object[] objects) {    throw new CacheException("Not allowed to update a NullCacheKey instance.");  }}

.
.

广告 广告

评论区