Mybatis的一级缓存,指的是SqlSession级别的缓存,默认开启;Mybatis的二级缓存,指的是SqlSessionFactory级别的缓存,须要配置。缓存是针对select来讲的。java
<configuration> <settings> <setting name="localCacheScope" value="SESSION|STATEMENT" /> </settings> </configuration>
localCacheScope用于配置一级缓存的范围,默认值是SESSION,表示SqlSession范围;sql
若是配置为STATEMENT,则表示SqlSession范围内的一个查询范围,但它并非一个Statement实例范围。数据库
STATEMENT举例:查询Student对象发送一次sql查询,紧接着再发一次sql查询关联的Teacher对象,这个完整过程称之为一个查询。apache
一级缓存默认开启,且没有全局关闭的配置开关。缓存
<select ... flushCache="false" useCache="true|false"/>
flushCache:同时影响了一级、二级缓存,flushCache=true,会致使清空本条sql(当前MappedStatement)的一级、二级缓存,注意是当前的,不影响其余的MappedStatement。网络
useCache:配置本条MappedStatement是否使用二级缓存,useCache=true,从二级缓存中获取,没有获取到,才从数据库中获取。数据结构
org.apache.ibatis.executor.CachingExecutor#query()方法源码:mybatis
Cache cache = ms.getCache(); if (cache != null) { // flushCache做用于二级缓存 flushCacheIfRequired(ms); // useCache做用于二级缓存 if (ms.isUseCache() && resultHandler == null) { ensureNoOutParams(ms, parameterObject, boundSql); @SuppressWarnings("unchecked") List<E> list = (List<E>) tcm.getObject(cache, key); if (list == null) { list = delegate.<E> query(ms, parameterObject, rowBounds, resultHandler, key, boundSql); tcm.putObject(cache, key, list); // issue #578 and #116 } return list; } } return delegate.<E> query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
org.apache.ibatis.executor.BaseExecutor#query()方法源码:app
if (queryStack == 0 && ms.isFlushCacheRequired()) { // flushCache做用于一级缓存 clearLocalCache(); }
在执行update、insert、delete、flushCache="true"、commit、rollback、LocalCacheScope.STATEMENT等状况下,一级缓存就都会被清空。ide
缓存其实基本数据结构就是一个HashMap,缓存中是否存在缓存数据,依赖key的生成策略。
org.apache.ibatis.executor.BaseExecutor.createCacheKey()源码。
@Override public CacheKey createCacheKey(MappedStatement ms, Object parameterObject, RowBounds rowBounds, BoundSql boundSql) { CacheKey cacheKey = new CacheKey(); cacheKey.update(ms.getId()); cacheKey.update(Integer.valueOf(rowBounds.getOffset())); cacheKey.update(Integer.valueOf(rowBounds.getLimit())); cacheKey.update(boundSql.getSql()); for (int i = 0; i < parameterMappings.size(); i++) { //... cacheKey.update(value); } } if (configuration.getEnvironment() != null) { // issue #176 cacheKey.update(configuration.getEnvironment().getId()); } return cacheKey; }
id:com.mybatis3.mappers.TeacherMapper.findTeacherById
key的生成策略:id + offset + limit + sql + param value + environment id,这些值都相同,生成的key就相同。
<configuration> <settings> <setting name="cacheEnabled" value="true|false" /> </settings> </configuration>
cacheEnabled=true表示二级缓存可用,可是要开启话,须要在Mapper.xml内配置。
<cache eviction="FIFO" flushInterval="60000" size="512" readOnly="true"/>
if (cacheEnabled) { executor = new CachingExecutor(executor); }
二级缓存经过CachingExecutor来实现,原理是缓存里存在,就返回,没有就调用Executor delegate到数据库中查询。
org.apache.ibatis.executor.CachingExecutor.query()源码。
@Override public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException { Cache cache = ms.getCache(); if (cache != null) { flushCacheIfRequired(ms); if (ms.isUseCache() && resultHandler == null) { ensureNoOutParams(ms, parameterObject, boundSql); @SuppressWarnings("unchecked") List<E> list = (List<E>) tcm.getObject(cache, key); if (list == null) { list = delegate.<E> query(ms, parameterObject, rowBounds, resultHandler, key, boundSql); tcm.putObject(cache, key, list); // issue #578 and #116 } return list; } } return delegate.<E> query(ms, parameterObject, rowBounds, resultHandler, key, boundSql); }
FIFO:First In First Out先进先出队列。
flushInterval="60000",间隔60秒清空缓存,这个间隔60秒,是被动触发的,而不是定时器轮询的。
size=512,表示队列最大512个长度,大于则移除队列最前面的元素,这里的长度指的是CacheKey的个数。
CacheKey的生成策略,和一级缓存相同,id + offset + limit + sql + param value + environment id。
readOnly="true",表示任何获取对象的操做,都将返回同一实例对象。若是readOnly="false",则每次返回该对象的拷贝对象,简单说就是序列化复制一份返回。
二级缓存有一个很是重要的空间划分策略:
namespace="com.mybatis3.mappers.TeacherMapper"
namespace="com.mybatis3.mappers.StudentMapper"
即,按照namespace划分,同一个namespace,同一个Cache空间,不一样的namespace,不一样的Cache空间。
每当执行insert、update、delete,flushCache=true时,二级缓存都会被清空。
版权提示:文章出自开源中国社区,若对文章感兴趣,可关注个人开源中国社区博客(http://my.oschina.net/zudajun)。(通过网络爬虫或转载的文章,常常丢失流程图、时序图,格式错乱等,仍是看原版的比较好)