一级缓存创建时机
/**
* DefaultSqlSessionFactory#openSession
*/
private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) {
Transaction tx = null;
try {
final Environment environment = configuration.getEnvironment();
final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment);
tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);
// 创建Executor执行器
final Executor executor = configuration.newExecutor(tx, execType);
// 创建DefaultSqlSession
return new DefaultSqlSession(configuration, executor, autoCommit);
} catch (Exception e) {
closeTransaction(tx); // may have fetched a connection so lets call close()
throw ExceptionFactory.wrapException("Error opening session. Cause: " + e, e);
} finally {
ErrorContext.instance().reset();
}
}
创建SqlSession时调用了这个方法创建了DefaultSqlSession,创建Executor执行器会生成一级缓存
/**
* Configuration#newExecutor
*/
public Executor newExecutor(Transaction transaction, ExecutorType executorType) {
executorType = executorType == null ? defaultExecutorType : executorType;
executorType = executorType == null ? ExecutorType.SIMPLE : executorType;
Executor executor;
if (ExecutorType.BATCH == executorType) {
// 批处理执行器
executor = new BatchExecutor(this, transaction);
} else if (ExecutorType.REUSE == executorType) {
// 可重用执行器
executor = new ReuseExecutor(this, transaction);
} else {
// 简单执行器
executor = new SimpleExecutor(this, transaction);
}
// 如果开启二级缓存(默认是开启的),则使用缓存执行器
if (cacheEnabled) {
executor = new CachingExecutor(executor);
}
// 执行插件
executor = (Executor) interceptorChain.pluginAll(executor);
return executor;
}
如果开启了二级缓存(二级缓存的全局开关,默认是开启的),会把执行器包装成缓存执行器CachingExecutor
,但真正执行操作的还是BatchExecutor、ReuseExecutor、SimpleExecutor
protected BaseExecutor(Configuration configuration, Transaction transaction) {
this.transaction = transaction;
this.deferredLoads = new ConcurrentLinkedQueue<>();
// 一级缓存
this.localCache = new PerpetualCache("LocalCache");
this.localOutputParameterCache = new PerpetualCache("LocalOutputParameterCache");
this.closed = false;
this.configuration = configuration;
this.wrapper = this;
}
BatchExecutor、ReuseExecutor、SimpleExecutor都继承了BaseExecutor类,所以在创建时会调用父类构造器,这时会创建一级缓存。一级缓存就是BaseExecutor的localCache
属性,所以也无法关闭一级缓存。
二级缓存创建时机
/**
* MapperAnnotationBuilder#parse
*/
public void parse() {
// 获取mapper接口的全路径
String resource = type.toString();
// 是否解析过该mapper接口
if (!configuration.isResourceLoaded(resource)) {
// 先解析mapper映射文件
loadXmlResource();
// 设置解析标识
configuration.addLoadedResource(resource);
// Mapper构建者助手
assistant.setCurrentNamespace(type.getName());
// 解析CacheNamespace注解
parseCache();
// 解析CacheNamespaceRef注解
parseCacheRef();
Method[] methods = type.getMethods();
for (Method method : methods) {
try {
// issue #237
if (!method.isBridge()) {
// 每个mapper接口中的方法,都解析成MappedStatement对象
parseStatement(method);
}
} catch (IncompleteElementException e) {
configuration.addIncompleteMethod(new MethodResolver(this, method));
}
}
}
parsePendingMethods();
}
在解析Mapper接口时,如果有CacheNamespace
或者CacheNamespaceRef
注解的话会创建二级缓存。CacheNamespace
是创建自己的缓存,CacheNamespaceRef
是指向其他Mapper接口的缓存。
缓存使用
/**
* 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, boundSql);
// 从二级缓存中查询数据
@SuppressWarnings("unchecked")
List<E> list = (List<E>) tcm.getObject(cache, key);
// 如果二级缓存中没有查询到数据,则查询数据库
if (list == null) {
// 委托给BaseExecutor执行
list = delegate.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
// 放入二级缓存
tcm.putObject(cache, key, list); // issue #578 and #116
}
return list;
}
}
// 委托给BaseExecutor执行
return delegate.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
}
在查询时,如果开启了二级缓存(默认是开启的),执行器也就是CachingExecutor
,会先去二级缓存中查询,有数据就直接返回;如果没有二级缓存,或者二级缓存查询不到,就委托真实的执行器查询
/**
* BaseExecutor#query
*/
@Override
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()) {
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();
}
// issue #601
deferredLoads.clear();
if (configuration.getLocalCacheScope() == LocalCacheScope.STATEMENT) {
// issue #482
clearLocalCache();
}
}
return list;
}
所以,Mybatis查询顺序:二级缓存 → 一级缓存 → 数据库。
根据源码可以发现,一级缓存是SqlSession级别,二级缓存是Mapper接口级别的。一级缓存是没办法关闭;二级缓存关闭可以把全局开关cacheEnabled
设置成 false,或者Mapper接口不加CacheNamespace
或者CacheNamespaceRef
注解