MyBatis一级缓存
问题:
1.一级缓存到底是什么?
2.一级缓存什么时候被创建?
3.一级缓存的工作流程是怎样的?
源码分析流程
一级缓存到底是什么?解答
由于一级缓存与SqlSession有关下面分析从SqlSession接口方法入手
1.找到SqlSession中与缓存相关的方法-clearCache();
2.进入SqlSessoin实现类DefaultSqlSession,发现实现clearCache()的代码是
private final Executor executor;
public void clearCache() {
this.executor.clearLocalCache();
}
3.进入 Executor的实现类BaseExecutor实现clearLocalCache();的代码是
protected PerpetualCache localCache;
protected PerpetualCache localOutputParameterCache;
public void clearLocalCache() {
if (!this.closed) {
this.localCache.clear();
this.localOutputParameterCache.clear();
}
}
3.进入PerpetualCache类clear();发现执行的是Map的clear方法;
private Map<Object, Object> cache = new HashMap();
public void clear() {
this.cache.clear();
}
4.由此发现问题一中一级缓存是什么的答案是:一级缓存就是一个Map集合,一个SqlSession中有一个。
一级缓存什么时候被创建?一级缓存的工作流程是怎样的?解答
一级缓存应该是在首次查询时候创建
1.进入Sqsession的实现类DefaultSqlSession中selectList(String statement, Object parameter, RowBounds rowBounds);底层调用了SQL执行器Executor的query方法。
public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) {
List var5;
try {
MappedStatement ms = this.configuration.getMappedStatement(statement);
var5 = this.executor.query(ms, this.wrapCollection(parameter), rowBounds, Executor.NO_RESULT_HANDLER);
} catch (Exception var9) {
throw ExceptionFactory.wrapException("Error querying database. Cause: " + var9, var9);
} finally {
ErrorContext.instance().reset();
}
- Executor的实现类BaseExecutor实现query方法,发现CacheKey与createCacheKey方法,
又源码得知CacheKey由MappedStatement(mapper映射封装实体类) +parameter(入参)+RowBounds (分页参数)构成;
Override
public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {
BoundSql boundSql = ms.getBoundSql(parameter);
//创建缓存
CacheKey key = createCacheKey(ms, parameter, rowBounds, boundSql);
return this.query(ms, parameter, rowBounds, resultHandler, key, boundSql);
}
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();
//MappedStatement 的 id
// id就是Sql语句的所在位置包名+类名+ SQL名称
cacheKey.update(ms.getId());
// offset 就是 从索引为0的第一条数据开始取 , limit 0,Integer.MAXVALUE
cacheKey.update(rowBounds.getOffset());
// limit 就是 Integer.MAXVALUE,从索引为0的第一条数据开始取Integer.MAXVALUE条数据
cacheKey.update(rowBounds.getLimit());
//具体的SQL语句
cacheKey.update(boundSql.getSql());
List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
TypeHandlerRegistry typeHandlerRegistry = ms.getConfiguration().getTypeHandlerRegistry();
Iterator var8 = parameterMappings.iterator();
while(var8.hasNext()) {
ParameterMapping parameterMapping = (ParameterMapping)var8.next();
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);
}
//后面是update 了 sql中带的参数
cacheKey.update(value);
}
}
if (this.configuration.getEnvironment() != null) {
cacheKey.update(this.configuration.getEnvironment().getId());
}
return cacheKey;
}
}
3.然后进入BaseExecutor的this.query(ms, parameter, rowBounds, resultHandler, key, boundSql);查询部分
//一级缓存
protected PerpetualCache localCache;
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 var8 = this.deferredLoads.iterator();
while(var8.hasNext()) {
BaseExecutor.DeferredLoad deferredLoad = (BaseExecutor.DeferredLoad)var8.next();
deferredLoad.load();
}
this.deferredLoads.clear();
if (this.configuration.getLocalCacheScope() == LocalCacheScope.STATEMENT) {
this.clearLocalCache();
}
}
return list;
}
}
private <E> List<E> queryFromDatabase(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {
this.localCache.putObject(key, ExecutionPlaceholder.EXECUTION_PLACEHOLDER);
List list;
try {
//查询获取最新的值
list = this.doQuery(ms, parameter, rowBounds, resultHandler, boundSql);
} finally {
//情况原有缓存
this.localCache.removeObject(key);
}
// 更新最新缓存结果值
this.localCache.putObject(key, list);
if (ms.getStatementType() == StatementType.CALLABLE) {
this.localOutputParameterCache.putObject(key, parameter);
}
return list;
}
4.解决了问题一级缓存是什么时候创建的:一级缓存实在第一次查询时创建的。
解决了问题一级缓存的工作流程是什么:第一次查询时后创建,第二次查询的时候会先去缓存中查询是否存在不存在则再次查询,并将查询结果放入缓存,如果存在则直接返回。