缓存
当我们发起一次数据库查询时,如果启用了二级缓存的话,MyBatis 首先会先从二级缓存中检索查询结果,如果缓存不命中则会继续检索一级缓存,只有在这两层缓存都不命中的情况下才会查询数据库,最后会以数据库返回的结果更新一级缓存和二级缓存。
一级缓存是基于 sqlSession 的,而 二级缓存是基于 mapper文件的namespace
也就是说多个sqlSession可以共享一个mapper中的二级缓存区域,并且如果两个mapper的namespace相同,即使是两个mapper,那么这两个mapper中执行sql查询到的数据也将存在相同的二级缓存区域中
先看看创建Executor的过程:
// 创建执行器(Configuration.newExecutor)
public Executor newExecutor(Transaction transaction, ExecutorType executorType) { Executor executor; if (ExecutorType.BATCH == executorType) { executor = new BatchExecutor(this, transaction); } else if (ExecutorType.REUSE == executorType) { executor = new ReuseExecutor(this, transaction); } else { executor = new SimpleExecutor(this, transaction); }
if (cacheEnabled) { //重点在这里,如果启用全局代理对象,返回Executor的Cache包装类对象 executor = new CachingExecutor(executor); } executor = (Executor) interceptorChain.pluginAll(executor); return executor;}
一级缓存
BaseExecutor 是一个抽象类,实现了 Executor 接口中声明的所有方法。BaseExecutor 抽象类引入了一级缓存支持,在相应方法实现中增加了对一级缓存的操作,因此该类的所有派生类都具备一级缓存的特性。一级缓存比较简单,具体实现是PerpetualCache
二级缓存
由上面创建执行器可以看出,二级缓存是需要单独配置的。
需要以下两步:全局配置:mapper配置: 多个Mapper共用一个Cache缓存对象(使用节点配置)详细实现:CachingExecutor其实是个代理模式,所有操作都是通过deleget来完成的。private Executor delegate;只有查询缓存是自己实现的。缓存策略:先看缓存的构建:
CacheBuilder
public Cache build() {
setDefaultImplementations();
Cache cache = newBaseCacheInstance(this.implementation, this.id);
..
return cache;
}
setDefaultImplementations:
this.implementation = PerpetualCache.class;
if (this.decorators.isEmpty())
this.decorators.add(LruCache.class);
}
可以看出二级缓存的默认实现也是PerpetualCache,另外缓存策略是通过装饰器模式来实现的,默认的缓存策略是LRU
完整的一级缓存、二级缓存的工作,其中一级缓存的实现真正是通过BaseExecutor实现。