org.apache.ibatis.executor.CachingExecutor#query() 先从MappedStatement中获取缓存对象(在xml文件中配置的cache的对象,这里即EhcacheCache类的对象 )
<cache type="org.mybatis.caches.ehcache.EhcacheCache"></cache>
- 如果缓存对象为空,则开始执行delegate.query(),这里的delegate就是封装的其他三种类型的Executer
- 如果缓存对象不为空,则尝试从
缓存取list。如果获取到的list为空,就执行delegate.query(),执行过后就将结果放入缓存中
public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql)
throws SQLException {
//从MappedStatement中获取缓存对象,一个sqlId对应一个MappedStatement的对象
Cache cache = ms.getCache();
if (cache != null) {
flushCacheIfRequired(ms);
if (ms.isUseCache() && resultHandler == null) {
ensureNoOutParams(ms, boundSql);
@SuppressWarnings("unchecked")
//尝试从缓存中获取查询结果
List<E> list = (List<E>) tcm.getObject(cache, key);
if (list == null) {
//缓存中获取查询结果为空,执行包装的Executer的query()方法
list = delegate.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
//将查询到的结果放到缓存中
tcm.putObject(cache, key, list); // issue #578 and #116
}
return list;
}
}
//如果获取到的缓存对象为空,则执行包装的Executer的query()方法
return delegate.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
}
以上是一个简单且不太准确的描述,那下面看下真正的缓存吧,
//尝试从缓存中获取查询结果
List<E> list = (List<E>) tcm.getObject(cache, key);
首先看下tcm,tcm是TransactionalCacheManager类的一个对象,也是CachingExecutor的一个属性。以下代码中,在创建以一个CachingExecutor对象的同时,也创建了一个TransactionalCacheManager类的对象。
public class CachingExecutor implements Executor {
private Executor delegate;
private TransactionalCacheManager tcm = new TransactionalCacheManager();
...
}
接着就看下TransactionalCacheManager.java类,这个类有一个以Cache为key,TransactionalCache为value的map类型的数据结构transactionalCaches 。也是在创建TransactionalCacheManager的时候,该对象就创建出来了
public class TransactionalCacheManager {
private Map<Cache, TransactionalCache> transactionalCaches = new HashMap<Cache, TransactionalCache>();
···
}
紧接着,还是要再看下TransactionalCache这个类,就只看下构造函数就行。封装了Cache对象,又多出一个map和一个set
public TransactionalCache(Cache delegate) {
this.delegate = delegate;
this.clearOnCommit = false;
this.entriesToAddOnCommit = new HashMap<Object, Object>();
this.entriesMissedInCache = new HashSet<Object>();
}
简单的了解下整个数据的结构,就看下tcm.getObject(cache, key); 这个方法
public Object getObject(Cache cache, CacheKey key) {
return getTransactionalCache(cache).getObject(key);
}
接着看下getTransactionalCache(cache); 这行代码就是根据cache对象获取TransactionalCache对象
private TransactionalCache getTransactionalCache(Cache cache) {
//transactionalCaches = new HashMap<Cache, TransactionalCache>(),transactionalCaches是一个以Cache为key,TransactionalCache为value的map类型的数据结构
TransactionalCache txCache = transactionalCaches.get(cache);
//如果获取到的txCache(TransactionalCache)是空的,则初始化,并放到transactionalCaches中去
if (txCache == null) {
txCache = new TransactionalCache(cache);
transactionalCaches.put(cache, txCache);
}
//返回txCache
return txCache;
}
再看下TransactionalCache.getObject(key); 就是从cache就获取缓存内容然后返回;
public Object getObject(Object key) {
// issue #116 这里的delegate就是ms的cache(具体看下TransactionalCache的构造函数)
Object object = delegate.getObject(key);
if (object == null) {
entriesMissedInCache.add(key);
}
// issue #146
if (clearOnCommit) {
return null;
} else {
return object;
}
}
以上是缓存的获取,接着看下缓存的添加
tcm.putObject(cache, key, list);
直接看下这个方法吧,
public void putObject(Cache cache, CacheKey key, Object value) {
getTransactionalCache(cache).putObject(key, value);
}
getTransactionalCache(cache) 方法就不说了,在读取缓存内容的时候已经说过了,就是从transactionalCaches中获取txCache对象;
看下TransactionalCache.putObject(key, value)方法吧。
public void putObject(Object key, Object object) {
//private Map<Object, Object> entriesToAddOnCommit;,将从数据库查出来的结果放到entriesToAddOnCommit中,并没有放到ms的cache中去
entriesToAddOnCommit.put(key, object);
}
将从数据库查出来的结果放到entriesToAddOnCommit中,并没有放到ms的cache中去,而读取的时候,却是通过TransactionalCache从ms的cache中读取的。别着急,看下org.apache.ibatis.executor.CachingExecutor#commit方法
public void commit(boolean required) throws SQLException {
delegate.commit(required);
tcm.commit();
}
看到熟悉的tcm,就看下tcm.commit();吧
public void commit() {
//transactionalCaches = new HashMap<Cache, TransactionalCache>();
//遍历transactionalCaches的values。执行其commit方法
for (TransactionalCache txCache : transactionalCaches.values()) {
txCache.commit();
}
}
那接着看txCache.commit();的方法
public void commit() {
if (clearOnCommit) {
delegate.clear();
}
flushPendingEntries();
reset();
}
接着是flushPendingEntries(); 循环entriesToAddOnCommit,放到cache中。至此,缓存的获取以及添加都差不多了
private void flushPendingEntries() {
//delegate是cache对象,看TransactionalCache的构造方法,循环entriesToAddOnCommit,放到cache中
for (Map.Entry<Object, Object> entry : entriesToAddOnCommit.entrySet()) {
delegate.putObject(entry.getKey(), entry.getValue());
}
for (Object entry : entriesMissedInCache) {
if (!entriesToAddOnCommit.containsKey(entry)) {
delegate.putObject(entry, null);
}
}
}