首先要知道,Mybatis中有一级缓存和二级缓存.
一级缓存是默认开启的,而且是不能关闭的,一级缓存是指SqlSession级别的缓存。 在同一个SqlSession中,相同的SQL语句,第一次查询会走数据库并将结果缓存,以后相同的查询直接从缓存中拿结果。
二级缓存是mapper级别的缓存,可以在不同sqlSession中共享,mapper以命名空间为单位创建缓存数据结构,需要手动开启,配置方式为:
mybatis:
mapper-locations: classpath:mapper/*.xml
type-aliases-package: ***
configuration:
cache-enabled: true --开启
1、mybatis的一级缓存和二级缓存实现细节
1、一级缓存和二级缓存配置后怎么体现在代码里的?
配置后是怎么实现的,我来扒一扒源码:
思路:sqlsession需要由SqlSessionFactory.openSession()开启,打开openSession()方法调用的是openSessionFromDataSource()
/**
DefaultSqlSessionFactory.openSessionFromDataSource()方法,创建sqlsession
*/
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执行器,比较常用的SimpleExecutor(使用一级缓存)和CachingExecutor(使用二级缓存),execType默认是SIMPLE
//进入configuration.newExecutor(tx, execType)方法看的到创建细节
final Executor executor = configuration.newExecutor(tx, execType);
//创建好的默认SqlSession,封装了配置信息configuration和执行器executor
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();
}
}
// 打开configuration.newExecutor(tx, execType)
public Executor newExecutor(Transaction transaction, ExecutorType executorType) {
// 获取executor的类型,如果没有指定,暂时默认SIMPLE
executorType = executorType == null ? defaultExecutorType : executorType;
executorType = executorType == null ? ExecutorType.SIMPLE : executorType;
Executor executor;
if (ExecutorType.BATCH == executorType) {
// BATCH 批量执行器
executor = new BatchExecutor(this, transaction);
} else if (ExecutorType.REUSE == executorType) {
executor = new ReuseExecutor(this, transaction);
} else {
executor = new SimpleExecutor(this, transaction);
}
//这里二级缓存开启后的体现,配置后cacheEnabled=true,就会创建CachingExecutor缓存执行器
if (cacheEnabled) {
executor = new CachingExecutor(executor);
}
executor = (Executor) interceptorChain.pluginAll(executor);
return executor;
}
2、mybatis的一级缓存和二级缓存怎么实现缓存的?
思路:接下来使用DefaultSqlSession.select()查询方法为例,说明一级缓存和二级缓存怎么实现缓存的
@Override
public void select(String statement, Object parameter, RowBounds rowBounds, ResultHandler handler) {
try {
/** configuration中维护了mappedStatements,这是mapper.xml文件解析后的信息封装保存在了configuration,
mappedStatements是在IOC容器初始化时,调用SqlSessionFactoryBean类的afterPropertiesSet()方法触发解析所有的mapper文件的。(建议去看看整个过程怎么来的)
可以通过String statement,一个类名.方法名的字符串查找到对应的Mapper方法封装后的MappedStatement
*/
MappedStatement ms = configuration.getMappedStatement(statement);
//根据创建sqlSession时,构建的是SimpleExecutor还是CachingExecutor分别进入不同的实现类,分别来看
executor.query(ms, wrapCollection(parameter), rowBounds, handler);
} catch (Exception e) {
throw ExceptionFactory.wrapException("Error querying database. Cause: " + e, e);
} finally {
ErrorContext.instance().reset();
}
}
1.如果是SimpleExecutor,会调用抽象父类BaseExecutor的实现
@Override
public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {、
//解析sql,把#{},${}替换成?,方便后续StatementHandler替换参数
BoundSql boundSql = ms.getBoundSql(parameter);
//创建缓存key,因为mybatis默认开启一级缓存,需要使用sql+参数创建缓存的key
CacheKey key = createCacheKey(ms, parameter, rowBounds, boundSql);
//查询
return query(ms, parameter, rowBounds, resultHandler, key, boundSql);
}
@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.");
}
//queryStack 查询层数, ms.isFlushCacheRequired()是否调用刷新缓存
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();
}
deferredLoads.clear();
if (configuration.getLocalCacheScope() == LocalCacheScope.STATEMENT) {
clearLocalCache();
}
}
return list;
}
2. 回过头在看看CachingExecutor的query方法实现
@Override
public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {
BoundSql boundSql = ms.getBoundSql(parameterObject);
CacheKey key = createCacheKey(ms, parameterObject, rowBounds, boundSql);
return query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
}
@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) {
//是否需要刷新缓存,需要配置flushCacheRequired=true
flushCacheIfRequired(ms);
if (ms.isUseCache() && resultHandler == null) {
ensureNoOutParams(ms, boundSql);
@SuppressWarnings("unchecked")
//查询缓存,这里缓存是保存在CachingExecutor的TransactionalCacheManager里的,在TransactionalCacheManager里面
//有个Map<Cache, TransactionalCache> transactionalCaches = new HashMap<>();
//二级缓存就是保存在这个map中的
List<E> list = (List<E>) tcm.getObject(cache, key);
if (list == null) {
//缓存中没有,再调用BaseExecutor的query查询
list = delegate.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
//将结果缓存
tcm.putObject(cache, key, list); // issue #578 and #116
}
return list;
}
}
return delegate.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
}