先可以参考下面的文章,写得比较详细: https://www.cnblogs.com/happyflyingpig/p/7739749.html
1.一级缓存的总结

如上图,当一个sqlSession第二次查询id为1的用户,本地缓存就被触发了,如果中间发现了修改,插入,删除并且提交了,一级缓存会被清空,这也展示了如何来验证以及缓存的情况
mybatis的一级缓存是基于sqlSession来缓存的。默认的情况下开启的,好处就是减少对数据库的压力
- 补充一点:一级缓存对应的源码实现
对应源码:org.apache.ibatis.executor.BaseExecutor#query
public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {
ErrorContext.instance().resource(ms.getResource()).activity("executing a query").object(ms.getId());
if (this.closed) {
throw new ExecutorException("Executor was closed.");
} else {
if (this.queryStack == 0 && ms.isFlushCacheRequired()) {
this.clearLocalCache();
}
List list;
try {
++this.queryStack;
//尝试从缓存中获取对象
list = resultHandler == null ? (List)this.localCache.getObject(key) : null;
if (list != null) {
//处理参数绑定
this.handleLocallyCachedOutputParameters(ms, key, parameter, boundSql);
} else {
list = this.queryFromDatabase(ms, parameter, rowBounds, resultHandler, key, boundSql);
}
} finally {
--this.queryStack;
}
if (this.queryStack == 0) {
Iterator i$ = this.deferredLoads.iterator();
while(i$.hasNext()) {
BaseExecutor.DeferredLoad deferredLoad = (BaseExecutor.DeferredLoad)i$.next();
deferredLoad.load();
}
this.deferredLoads.clear();
if (this.configuration.getLocalCacheScope() == LocalCacheScope.STATEMENT) {
this.clearLocalCache();
}
}
return list;
}
}
- CacheKey怎么生成
对应着mybatis的源码org.apache.ibatis.executor.BaseExecutor#createCacheKey
对一个cashKey的生成方式如下:
public CacheKey createCacheKey(MappedStatement ms, Object parameterObject, RowBounds rowBounds, BoundSql boundSql) {
if (this.closed) {
throw new ExecutorException("Executor was closed.");
} else {
CacheKey cacheKey = new CacheKey();
//传入的statementId
cacheKey.update(ms.getId());
//对应着sql从哪条记录开始查
cacheKey.update(rowBounds.getOffset());
//对应着sql查几条
cacheKey.update(rowBounds.getLimit());
//对应着sql语句
cacheKey.update(boundSql.getSql());
//得到参数映射信息
List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
TypeHandlerRegistry typeHandlerRegistry = ms.getConfiguration().getTypeHandlerRegistry();
Iterator i$ = parameterMappings.iterator();
while(i$.hasNext()) {
ParameterMapping parameterMapping = (ParameterMapping)i$.next();
//参数模式为out类型,这种情况在执行存储过程和方法的时候存在
if (parameterMapping.getMode() != ParameterMode.OUT) {
String propertyName = parameterMapping.getProperty();
Object value;
if (boundSql.hasAdditionalParameter(propertyName)) {
value = boundSql.getAdditionalParameter(propertyName);
} else if (parameterObject == null) {
value = null;
} else if
//是否有字段的映射处理器
(typeHandlerRegistry.hasTypeHandler(parameterObject.getClass())) {
value = parameterObject;
} else {
MetaObject metaObject = this.configuration.newMetaObject(parameterObject);
value = metaObject.getValue(propertyName);
}
//参数的信息
cacheKey.update(value);
}
}
if (this.configuration.getEnvironment() != null) {
//配置环境的id
cacheKey.update(this.configuration.getEnvironment().getId());
}
return cacheKey;
}
}
总结: CashKey的维度
- 1.传入的statementId
- 2.结果集中的结果范围
- 3.sql语句
- 4.传递的参数信息
- 5.配置开发环境id
2.二级缓存的总结
总结:
MyBatis的二级缓存是mapper级别的缓存,它可以提高对数据库查询的效率,以提高应用的性能。mybatis的二级缓存默认是关闭,一般不建议使用, 可以使用redis或者第三方开启的缓存框架来使用。
二级缓存的问题:
- 缓存是以namespace为单位的,不同namespace下的操作互不影响。
insert,update,delete操作会清空所在namespace下的全部缓存。- 还可能造成脏数据的产生
模拟脏数据的产生

BlogMapper的有一个查询语句,其中blog中包含了Author的对象,此时AuthorMapper的有一个方法更新了Author的信息,此时blogMapper的对象中blog关联的author并不会更新,当二级缓存就会命中,那么此时的author的信息就不是最新的,脏数据就会产生
可以通过修改mybatis的配置文件来开启缓存
<setting name="cacheEnabled" value="true" />
如果某个mapper文件需要使用,需要配置上如下的配置,LRU表示最近最少使用的,一处最长时间不用的对象
<cache eviction="LRU" flushInterval="100000" readOnly="true" size="1024"/>
在statement中设置useCache=false可以禁用当前select语句的二级缓存,即每次查询都会发出sql去查询,默认情况是true,即该sql使用二级缓存。
<!--可以通过设置useCache来规定这个sql是否开启缓存,ture是开启,false是关闭,默认情况是true-->
<select id="selectAllStudents" resultMap="studentMap" useCache="true">
SELECT id, name, age FROM student
</select>
网友评论