深入了解MyBatis二级缓存
一、创建Cache的完整过程
我们从SqlSessionFactoryBuilder解析mybatis-config.xml配置文件开始:
Reader reader = Resources.getResourceAsReader("mybatis-config.xml");
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(reader);
然后是:
XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);
return build(parser.parse());
看parser.parse()方法:
parseConfiguration(parser.evalNode("/configuration"));
看处理Mapper.xml文件的位置:
mapperElement(root.evalNode("mappers"));
看处理Mapper.xml的XMLMapperBuilder:
XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration,
resource, configuration.getSqlFragments());
mapperParser.parse();
继续看parse方法:
configurationElement(parser.evalNode("/mapper"));
到这里:
String namespace = context.getStringAttribute("namespace");
if (namespace.equals("")) {
throw new BuilderException("Mapper's namespace cannot be empty");
}
builderAssistant.setCurrentNamespace(namespace);
cacheRefElement(context.evalNode("cache-ref"));
cacheElement(context.evalNode("cache"));
从这里看到namespace就是xml中
看看MyBatis如何处理
private void cacheElement(XNode context) throws Exception {
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");
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);
}
}
从源码可以看到MyBatis读取了那些属性,而且很容易可以到这些属性的默认值。
创建Java的cache对象方法为builderAssistant.useNewCache,我们看看这段代码:
public Cache useNewCache(Class extends Cache> typeClass,
Class extends Cache> evictionClass,
Long flushInterval,
Integer size,
boolean readWrite,
boolean blocking,
Properties props) {
typeClass = valueOrDefault(typeClass, PerpetualCache.class);
evictionClass = valueOrDefault(evictionClass, LruCache.class);
Cache cache = new CacheBuilder(currentNamespace)
.implementation(typeClass)
.addDecorator(evictionClass)
.clearInterval(flushInterval)
.size(size)
.readWrite(readWrite)
.blocking(blocking)
.properties(props)
.build();
configuration.addCache(cache);
currentCache = cache;
return cache;
}
从调用该方法的地方,我们可以看到并没有使用返回值cache,在后面的过程中创建MappedStatement的时候使用了currentCache。
二、使用Cache过程
在系统中,使用Cache的地方在CachingExecutor中:
@Override
public
MappedStatement ms, Object parameterObject,
RowBounds rowBounds, ResultHandler resultHandler,
CacheKey key, BoundSql boundSql) throws SQLException {
Cache cache = ms.getCache();
获取cache后,先判断是否有二级缓存。
只有通过
if (cache != null) {
如果cache存在,那么会根据sql配置(