一 、概要
一级缓存是会话级别的,也就是一个SqlSession里的,不同会话的一级缓存是互相隔离,不可见的,当会话结束,一级缓存也就被销毁。
一级缓存维护在BaseExecutor类中:
二 、一级缓存开启和关闭
一级缓存默认是开启的。
如何关闭呢?
一级缓存是没有关闭配置的,也就是说代码中都会设置一级缓存,我们配置的关闭缓存实际是代码中清空一级缓存而已,看源码:
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 (closed) {
throw new ExecutorException("Executor was closed.");
}
if (queryStack == 0 && ms.isFlushCacheRequired()) {
//flushCacheRequired如果设置为true,则清空一级缓存
clearLocalCache();
}
List<E> list;
try {
queryStack++;
//从一级缓存获取数据
list = resultHandler == null ? (List<E>) localCache.getObject(key) : null;
if (list != null) {
handleLocallyCachedOutputParameters(ms, key, parameter, boundSql);
} else {
//缓存中没有则查询数据库
list = queryFromDatabase(ms, parameter, rowBounds, resultHandler, key, boundSql);
}
} finally {
queryStack--;
}
if (queryStack == 0) {
for (DeferredLoad deferredLoad : deferredLoads) {
deferredLoad.load();
}
deferredLoads.clear();
if (configuration.getLocalCacheScope() == LocalCacheScope.STATEMENT) {
//如果localCacheScope配置的不是session级别则清空一级缓存
clearLocalCache();
}
}
return list;
}
//查询数据库
private <E> List<E> queryFromDatabase(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {
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;
}
可以看到每次查询数据库后都会把数据放入到一级缓存中,如果localCacheScope设置的不是session而是statement,那么则会清空一级缓存。如果flushCache设置为true,也会清空缓存,去查数据库。
三 、命中条件
1 同一个SqlSession
因为一级缓存是会话级别,这个没啥好说的。
2 localCacheScope设置的值为SESSION
如果不是SESSION则第一次查询则清空缓存,第二次查询就不会命中缓存。
3 flushCache没有设置为true
4 相同的statementId
StatementId就是指的同一个Mapper.xml文件,同一个id,哪怕sql一样,如果方法名不一样,也不会命中。
5 sql的参数一样
6 未执行update操作
mybatis中update操作指的是update、delete、insert,如果同一个会话里有update操作,则会删除一级缓存。
BaseExecutor#updat()
四 、Spring整合Mybatis一级缓存会失效?
spring整合后,每次查询,都会新建一个会话,所以一级缓存不会命中。如果开启事务的化,就会用的同一个会话,会命中一级缓存。
五 、和二级缓存关系
二级缓存是应用级别的,当查询时候,是先查询二级缓存,如果未命中则查询一级缓存,一级缓存未命中,则查询数据库。