下面来聊一聊Mybatis中的一级缓存和二级缓存
1.Mybatis中的一级缓存
下面提几个问题来说明:
(1).为什么Mybatis中是默认开启一级缓存的,而且只能在单独的sqlSession中有效?
在创建SqlSession的时候,有如下代码:
SqlSession sqlSession=sqlSessionFactory.openSession();
--> return openSessionFromDataSource(configuration.getDefaultExecutorType(), null, false);
--> final Executor executor = configuration.newExecutor(tx, execType);
newExecutor定义如下:
public Executor newExecutor(Transaction transaction, ExecutorType executorType) {
executorType = executorType == null ? defaultExecutorType : executorType;
executorType = executorType == null ? ExecutorType.SIMPLE : 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) { //这里cacheEnabled默认是true,即默认executor是CachingExecutor
executor = new CachingExecutor(executor);
}
executor = (Executor) interceptorChain.pluginAll(executor);
return executor;
}
下面对查询代码进行代码跟踪
return executor.query(ms, wrapCollection(parameter), rowBounds, Executor.NO_RESULT_HANDLER); //selectList方法
-->return delegate.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql); //CachingExector
--> list = resultHandler == null ? (List<E>) localCache.getObject(key) : null; //这的localCache就是一级缓存
--> list = queryFromDatabase(ms, parameter, rowBounds, resultHandler, key, boundSql);
--> localCache.putObject(key, list); //添加一级缓存
所以一级缓存默认是开启的的,而且只能在单独的sqlSession中有效。
(2)为什么在一个sesion中进行增删改会使一级缓存失效?
下面跟踪DefaultSqlSession中的update方法:
return executor.update(ms, wrapCollection(parameter));
-->return delegate.update(ms, parameterObject);
--> clearLocalCache(); //这里会清除一级缓存
--> localCache.clear();
2.Mybatis中的二级缓存
1.二级缓存的作用域,为什么只有在commit后才有效?
Mybatis中的二级级缓存是需要手动配置的,默认是不开启的。
下面看一下配置的过程
parseConfiguration(parser.evalNode("/configuration"));
-->mapperElement(root.evalNode("mappers"));
-->XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, resource, configuration.getSqlFragments());
-->mapperParser.parse();
--> configurationElement(parser.evalNode("/mapper"));
-->cacheElement(context.evalNode("cache"));
cacheElement定义如下:
private void cacheElement(XNode context) {
if (context != null) {
String type = context.getStringAttribute("type", "PERPETUAL");
Class<? extends Cache> typeClass = typeAliasRegistry.resolveAlias(type);
String eviction = context.getStringAttribute("eviction", "LRU"); //清除策略
Class<? extends Cache> evictionClass = typeAliasRegistry.resolveAlias(eviction);
Long flushInterval = context.getLongAttribute("flushInterval"); //刷新间隔
Integer size = context.getIntAttribute("size"); //size
boolean readWrite = !context.getBooleanAttribute("readOnly", false); //只读
boolean blocking = context.getBooleanAttribute("blocking", false);
Properties props = context.getChildrenAsProperties();
builderAssistant.useNewCache(typeClass, evictionClass, flushInterval, size, readWrite, blocking, props);
}
}
关注这一句
builderAssistant.useNewCache(typeClass, evictionClass, flushInterval, size, readWrite, blocking, props);
--> configuration.addCache(cache);
--> currentCache = cache; //这个currentCache最后也被添加到statementBuilder中去
可以看出,configuartion中的cache属性就是二级缓存的相关配置。
下面再跟踪一下sqlSession下的selectList方法
return query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
--> Cache cache = ms.getCache(); //此处就是获取二级缓存
--> List<E> list = (List<E>) tcm.getObject(cache, key);
-->return getTransactionalCache(cache).getObject(key);
--> Object object = delegate.getObject(key); //注意这里拿的是delegate的key
--> tcm.putObject(cache, key, list); // issue #578 and #116
--> getTransactionalCache(cache).putObject(key, value);
-->entriesToAddOnCommit.put(key, object); //注意这里加的是entriesToAddOnCommit的key,value
看一下commit的方法:
executor.commit(isCommitOrRollbackRequired(force));
--> tcm.commit();
-->txCache.commit();
--> flushPendingEntries();
//flushPendingEntries方法如下
private void flushPendingEntries() {
for (Map.Entry<Object, Object> entry : entriesToAddOnCommit.entrySet()) {
delegate.putObject(entry.getKey(), entry.getValue()); //关键这里:将entriesToAddOnCommit中的k-v添加到delegate,这里就解释了为什么在commit后二级缓存才有效。
}
for (Object entry : entriesMissedInCache) {
if (!entriesToAddOnCommit.containsKey(entry)) {
delegate.putObject(entry, null);
}
}
}
又因为tcm是被final修饰的,所以作用域是整个namespace.
网友评论