mybatis二级缓存工作机制
在mybatis的主配置文件中,启动二级缓存配置
<settings>
<setting name="cacheEnabled" value="true"/>
</settings>
这个配置会再以后生成Executor的时候使用CachingExecutor而非 BaseExecutor
然后在映射的xml配置文件如UserMapper.xml中 对该实体启用缓存,简单点写直接添加
<cache/>
即可。至此配置完成。
在完成以上配置之后,对缓存进行测试
测试的核心代码
SqlSession session = sqlSessionFactory.openSession();
List<User> allUsers = session.selectList("com.test.model.User.queryUserByName", "%test%", new RowBounds(1, 1));
session.commit();
List<User> allUsers3 = sqlSessionFactory.openSession().selectList("com.test.model.User.queryUserByName", "%test%", new RowBounds(1, 1));
理论上将,这种方式查询数据请求,在mybatis中肯定是可以直接从二级缓存中取数据的。
请看如下的图(网上找的,觉得还不错)。
其中CachingExecutor类的query方法
public List query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {
if (ms != null) {
Cache cache = ms.getCache();
if (cache != null) {
flushCacheIfRequired(ms);
cache.getReadWriteLock().readLock().lock();
try {
if (ms.isUseCache() && resultHandler == null) {
CacheKey key = createCacheKey(ms, parameterObject, rowBounds);
final List cachedList = (List) cache.getObject(key);
if (cachedList != null) {
return cachedList;
} else {
List list = delegate.query(ms, parameterObject, rowBounds, resultHandler);
tcm.putObject(cache, key, list);
return list;
}
} else {
return delegate.query(ms, parameterObject, rowBounds, resultHandler);
}
} finally {
cache.getReadWriteLock().readLock().unlock();
}
}
}
return delegate.query(ms, parameterObject, rowBounds, resultHandler);
}
查询请求来的时候,会首先从MappedStatement中取出缓存实例Cache,若开启了二级缓存的,则先从二级缓存中查找数据,找到即直接返回结果,若没有找到,则使用delegate(叫做代理业好,装饰器也好)所代理的查询(此处为BaseExecutor)来进行查询,代理的具体查询就不说了,不是本文的重点。结合上图以及代码,可以看出tcm.putObject(cache, key, list);这句话是将查询处理的结果放入缓存,其中tcm是在CachingExecutor中定义的private TransactionalCacheManager tcm = new TransactionalCacheManager();
然后我们查看TransactionalCacheManager的putObject(cache, key, list)方法,如下
public class TransactionalCacheManager {
private Map<Cache, TransactionalCache> transactionalCaches = new HashMap<Cache, TransactionalCache>();
public void putObject(Cache cache, CacheKey key, Object value) {
getTransactionalCache(cache).putObject(key, value);
}
}
继续查看TransactionalCache的源码,可以看到,在TransactionalCache的putObject(Object key, Object object)方法中,并不是直接将结果集合放入缓存中,而是加入到一个队列中,这个队列会再发起这次请求的SqlSession提交(commit)或关闭(close)的时候,调用到TransactionalCache的commit方法,从而调用到内部类AddEntry的commit方法,将结果集合存放到缓存中。
public class TransactionalCache implements Cache {
private Cache delegate;
private Map<Object, AddEntry> entriesToAddOnCommit;
public void putObject(Object key, Object object) {
entriesToRemoveOnCommit.remove(key);
entriesToAddOnCommit.put(key, new AddEntry(delegate, key, object));
}
public void commit() {
delegate.getReadWriteLock().writeLock().lock();
try {
if (clearOnCommit) {
delegate.clear();
} else {
for (RemoveEntry entry : entriesToRemoveOnCommit.values()) {
entry.commit();
}
}
for (AddEntry entry : entriesToAddOnCommit.values()) {
entry.commit();
}
reset();
} finally {
delegate.getReadWriteLock().writeLock().unlock();
}
}
private static class AddEntry {
private Cache cache;
private Object key;
private Object value;
public AddEntry(Cache cache, Object key, Object value) {
this.cache = cache;
this.key = key;
this.value = value;
}
public void commit() {
cache.putObject(key, value);
}
}
}
下图为将结果集合存入缓存的过程。