[[toc]]

第五节 缓存的基本原理

1、Cache接口

①Cache接口的重要地位

org.apache.ibatis.cache.Cache接口:所有缓存都必须实现的顶级接口
05.缓存的基本原理 - 图1

②Cache接口中的方法

05.缓存的基本原理 - 图2

方法名 作用
putObject() 将对象存入缓存
getObject() 从缓存中取出对象
removeObject() 从缓存中删除对象

③缓存的本质

根据Cache接口中方法的声明我们能够看到,缓存的本质是一个Map。

2、PerpetualCache

05.缓存的基本原理 - 图3

org.apache.ibatis.cache.impl.PerpetualCache是Mybatis的默认缓存,也是Cache接口的默认实现。Mybatis一级缓存和自带的二级缓存都是通过PerpetualCache来操作缓存数据的。但是这就奇怪了,同样是PerpetualCache这个类,怎么能区分出来两种不同级别的缓存呢?
其实很简单,调用者不同。

  • 一级缓存:由BaseExecutor调用PerpetualCache
  • 二级缓存:由CachingExecutor调用PerpetualCache,而CachingExecutor可以看做是对BaseExecutor的装饰

3、一级缓存机制

05.缓存的基本原理 - 图4

org.apache.ibatis.executor.BaseExecutor类中的关键方法:

①query()方法

public List 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.”);
}
if (queryStack == 0 && ms.isFlushCacheRequired()) {
clearLocalCache();
}
List list;
try {
queryStack++;

  1. // 尝试从本地缓存中获取数据<br /> list = resultHandler == null ? (List<E>) localCache.getObject(key) : null;
  2. if (list != null) {<br /> handleLocallyCachedOutputParameters(ms, key, parameter, boundSql);<br /> } else {
  3. // 如果本地缓存中没有查询到数据,则查询数据库<br /> list = queryFromDatabase(ms, parameter, rowBounds, resultHandler, key, boundSql);<br /> }<br /> } finally {<br /> queryStack--;<br /> }<br /> if (queryStack == 0) {<br /> for (org.apache.ibatis.executor.BaseExecutor.DeferredLoad deferredLoad : deferredLoads) {<br /> deferredLoad.load();<br /> }<br /> // issue #601<br /> deferredLoads.clear();<br /> if (configuration.getLocalCacheScope() == LocalCacheScope.STATEMENT) {<br /> // issue #482<br /> clearLocalCache();<br /> }<br /> }<br /> return list;<br />}

②queryFromDatabase()方法

private List queryFromDatabase(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {
List list;
localCache.putObject(key, EXECUTION_PLACEHOLDER);
try {

    // 从数据库中查询数据<br />        list = doQuery(ms, parameter, rowBounds, resultHandler, boundSql);<br />    } finally {<br />        localCache.removeObject(key);<br />    }

// 将数据存入本地缓存<br />    localCache.putObject(key, list);<br />    if (ms.getStatementType() == StatementType.CALLABLE) {<br />        localOutputParameterCache.putObject(key, parameter);<br />    }<br />    return list;<br />}

4、二级缓存机制

05.缓存的基本原理 - 图5

下面我们来看看CachingExecutor类中的query()方法在不同情况下使用的具体缓存对象:

①未开启二级缓存

05.缓存的基本原理 - 图6

②使用自带二级缓存

05.缓存的基本原理 - 图7

③使用EHCache

05.缓存的基本原理 - 图8

上一节 回目录