mybatis源码之二 缓存
1:一级缓存设置
1.1:mybatis-config文件配置
mybatis-config.png1.2: 测试代码
cache-test.png1.3:测试结果
mybatis-result.png代码执行两个相同查询语句,mybatis日志打印一次,第二次查询走了缓存。
2:从源码分析同一个session如何走缓存,分析如下
session.selectOne(…………) ->
org.apache.ibatis.session.defaults.DefaultSqlSession#selectOne(…………)->
………………
org.apache.ibatis.session.defaults.DefaultSqlSession#selectList(………………)->
……………………
public <E> List<E> query(…………) throws SQLException {
BoundSql boundSql = ms.getBoundSql(parameterObject);
//生成缓存key
CacheKey key = createCacheKey(ms, parameterObject, rowBounds, boundSql);
return query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
}
继续执行下面方法生成缓存key,
key规则=ms.getId()+rowBounds.getOffset()+rowBounds.getLimit()+boundSql.getSql()
org.apache.ibatis.executor.BaseExecutor#createCacheKey(MappedStatement ms, Object parameterObject, RowBounds rowBounds, BoundSql boundSql) {
if (closed) {
throw new ExecutorException("Executor was closed.");
}
CacheKey cacheKey = new CacheKey();
cacheKey.update(ms.getId());
cacheKey.update(rowBounds.getOffset());
cacheKey.update(rowBounds.getLimit());
cacheKey.update(boundSql.getSql());
………………
return cacheKey;
}
继续执行以下方法
org.apache.ibatis.executor.CachingExecutor#query(…………){
//从缓存执行语句中获取
Cache cache = ms.getCache();
//获取到缓存从缓存获取
if (cache != null) {
flushCacheIfRequired(ms);
if (ms.isUseCache() && resultHandler == null) {
ensureNoOutParams(ms, boundSql);
@SuppressWarnings("unchecked")
List<E> list = (List<E>) tcm.getObject(cache, key);
if (list == null) {
list = delegate.query(………………);
tcm.putObject(cache, key, list); // issue #578 and #116
}
return list;
}
}
//缓存中没有 执行以下方法 该方法真正执行sql 并打印语句
return delegate.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
}
继续执行以下方法 查询数据
org.apache.ibatis.executor.BaseExecutor#query(……………………) {
…………………………
List<E> list;
try {
queryStack++;
//从缓存中获取
list = resultHandler == null ? (List<E>) localCache.getObject(key) : null;
if (list != null) {
handleLocallyCachedOutputParameters(ms, key, parameter, boundSql);
} else {
//为空执行sql
list = queryFromDatabase(ms, parameter, rowBounds, resultHandler, key, boundSql);
}
} finally {
queryStack--;
}
……………………
//如果一级缓存设置STATEMENT 清除缓存每次都是重新执行查询
if (configuration.getLocalCacheScope() == LocalCacheScope.STATEMENT) {
// issue #482
clearLocalCache();
}
}
return list;
}
继续执行以下方法:把数据保存在缓存中
org.apache.ibatis.executor.BaseExecutor#queryFromDatabase(………………){
List<E> list;
localCache.putObject(key, EXECUTION_PLACEHOLDER);
try {
//获取数据
list = doQuery(ms, parameter, rowBounds, resultHandler, boundSql);
} finally {
//清空缓存
localCache.removeObject(key);
}
//设置缓存
localCache.putObject(key, list);
if (ms.getStatementType() == StatementType.CALLABLE) {
localOutputParameterCache.putObject(key, parameter);
}
return list;
}
3:session提交可以清除缓存
commit-result.png执行commit,执行相同参数查询,打印出两条日志,从代码里面查看是把缓存清空,代码比较简单,可以自行跟踪。
4:查看一级缓存默认配置源码
org.apache.ibatis.builder.xml.XMLConfigBuilder#settingsElement(…………){
…………………………
//一级缓存默认是true 开启
configuration.setCacheEnabled(booleanValueOf(props.getProperty("cacheEnabled"), true));
………………
//一级缓存默认是session级别
configuration.setLocalCacheScope(LocalCacheScope.valueOf(props.getProperty("localCacheScope", "SESSION")));
………………
}
5:总结
mybatis有一级和二级缓存,一级缓存默认开启,二级缓存默认关闭不推荐使用,一级缓存默认开启会话级别,也可以设置STATEMENT级别,如果设置STATEMENT同一个session每次查询不会缓存,都是直接执行sql
网友评论