本篇文章主要介绍了"mybatis缓存的使用及理解",主要涉及到mybatis方面的内容,对于移动开发感兴趣的同学可以参考一下:
和hibernate一样,mybatis也有缓存机制
一级缓存是基于 PerpetualCache(mybatis自带)的 HashMap 本地缓存,作用范围为...
和hibernate一样,mybatis也有缓存机制
一级缓存是基于 PerpetualCache(mybatis自带)的 HashMap 本地缓存,作用范围为session,所以当session commit或close后,缓存就会被清空
二级缓存默认也是基于 PerpetualCache,但是可以为其制定存储源,比如ehcache
一级缓存缓存的是SQL语句,而二级缓存缓存的是结果对象,看如下例子(mybatis的日志级别设为debug)
?
1
2
3
4
5
6
7
8
List
users = sqlSession.selectList("com.my.mapper.UserMapper.getUser", "jack");
System.out.println(users);
//sqlSession.commit();①
List users2 = sqlSession.selectList("com.my.mapper.UserMapper.getUser", "jack");//②admin
System.out.println(users);
结果是只发起一次SQL语句,如果我们把②出的参数jack改为admin,发现还是只发起一次SQL语句,但是会设置不同参数
如果把①处去掉注释,会发现不会有缓存了
下面就来启用二级缓存
在配置文件中启用二级缓存
?
1
在需要进行缓存的mapper文件UserMapper.xml中加上
?
1
-->
用上面那个会输出更加详细的日志,下面的不会
需要用到ehcache.jar,下载地址:http://sourceforge.net/projects/ehcache/files/ehcache/ehcache-2.7.0/ehcache-2.7.0-distribution.tar.gz/download
mybatis-ehcache.jar下载地址:http://code.google.com/p/mybatis/downloads/detail?name=mybatis-ehcache-1.0.2-SNAPSHOT-bundle.zip&can=3&q=Product%3DCache
--尽量避免使用二级缓存技术,多表操作 的业务场景下
一、创建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中元素的属性。然后下面是先后处理的cache-ref和cache,后面的cache会覆盖前面的cache-ref,但是如果一开始cache-ref没有找到引用的cache,他就不会被覆盖,会一直到最后处理完成为止,最后如果存在cache,反而会被cache-ref覆盖。这里是不是看着有点晕、有点乱?所以千万别同时配置这两个,实际上也很少有人会这么做。
看看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 List query(
MappedStatement ms, Object parameterObject,
RowBounds rowBounds, ResultHandler resultHandler,
CacheKey key, BoundSql boundSql) throws SQLException {
Cache cache = ms.getCache();
获取cache后,先判断是否有二级缓存。
只有通过,或@CacheNamespace,@CacheNamespaceRef标记使用缓存的Mapper.xml或Mapper接口(同一个namespace,不能同时使用)才会有二级缓存。
if (cache != null) {
如果cache存在,那么会根据sql配置(,