Mybaits源码解析-缓存

42 阅读2分钟

Cache接口

使用过Mybatis的基本上都知道其有一级缓存和二级缓存,本质上都是实现的Cache接口。

image.png

/*
 *    Copyright 2009-2023 the original author or authors.
 *
 *    Licensed under the Apache License, Version 2.0 (the "License");
 *    you may not use this file except in compliance with the License.
 *    You may obtain a copy of the License at
 *
 *       https://www.apache.org/licenses/LICENSE-2.0
 *
 *    Unless required by applicable law or agreed to in writing, software
 *    distributed under the License is distributed on an "AS IS" BASIS,
 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *    See the License for the specific language governing permissions and
 *    limitations under the License.
 */
package org.apache.ibatis.cache;

import java.util.concurrent.locks.ReadWriteLock;

/**
 * SPI for cache providers.
 * <p>
 * One instance of cache will be created for each namespace.
 * <p>
 * The cache implementation must have a constructor that receives the cache id as an String parameter.
 * <p>
 * MyBatis will pass the namespace as id to the constructor.
 *
 * <pre>
 * public MyCache(final String id) {
 *   if (id == null) {
 *     throw new IllegalArgumentException("Cache instances require an ID");
 *   }
 *   this.id = id;
 *   initialize();
 * }
 * </pre>
 *
 * @author Clinton Begin
 */

public interface Cache {

  /**
   * 缓存的id
   * @return The identifier of this cache
   */
  String getId();

  /**
   *添加缓存数据
   * key ==>CacheKey
   * value 是查询的结果
   * @param key
   *          Can be any object but usually it is a {@link CacheKey}
   * @param value
   *          The result of a select.
   */
  void putObject(Object key, Object value);

  /**
   *
   * 根据缓存key查询结果
   * @param key
   *          The key
   *
   * @return The object stored in the cache.
   */
  Object getObject(Object key);

  /**
   * 删除缓存
   * As of 3.3.0 this method is only called during a rollback for any previous value that was missing in the cache. This
   * lets any blocking cache to release the lock that may have previously put on the key. A blocking cache puts a lock
   * when a value is null and releases it when the value is back again. This way other threads will wait for the value
   * to be available instead of hitting the database.
   *
   * @param key
   *          The key
   *
   * @return Not used
   */
  Object removeObject(Object key);

  /**
   * 清楚缓存
   * Clears this cache instance.
   */
  void clear();

  /**
   * 缓存大小
   * Optional. This method is not called by the core.
   *
   * @return The number of elements stored in the cache (not its capacity).
   */
  int getSize();

  /**
   * 锁
   * Optional. As of 3.2.6 this method is no longer called by the core.
   * <p>
   * Any locking needed by the cache must be provided internally by the cache provider.
   *
   * @return A ReadWriteLock
   */
  default ReadWriteLock getReadWriteLock() {
    return null;
  }

}

可以从包的命名看出来,缓存的基础实现是PerpetualCache类,其他的是通过装饰器模式实现的,例如BlockingCache

image.png

image.png

一级缓存

一级缓存是Session级别的,默认开启。

一级缓存的创建

一级缓存在构建SqlSession的时候,创建执行器SimpleExecutor构造方法的时候创建

image.png

image.png

一级缓存的关闭

设置LocalCacheScope为STATEMENT

image.png 上图逻辑LocalCacheScope为STATEMENT会清除一级缓存 image.png

一级缓存的调用

在执行器BaseExcutor的query()方法中调用

image.png

二级缓存

一级缓存是session级别的,当跨session的时候一级缓存会失去作用,这时候可以使用二级缓存。二级缓存是应用级别的,可以看成是mapper namespace级别的

二级缓存开关

二级缓存默认开启 image.png value 为false 关闭二级缓存 image.png 同时也要在mapper文件中配置cache标签

image.png 如果不想某个查询走二级缓存,可以配置参数关闭

image.png

二级缓存代码调用

如下图所示,cache不为空走二级缓存

image.png