Mybatis源码解析(二)
一、我们从下面这段代码开始
@Test
public void testCahce1() {
UserMapper mapper = session.getMapper(UserMapper.class);
User user = mapper.selectByid(1);
session.close();
User user2 = sqlSessionFactory.openSession(true)
.getMapper(UserMapper.class)
.selectByid(1);
System.out.println(user == user2);
System.out.println(user);
System.out.println(user2);
}
流程图
二、源码分析(此处只分析主要代码,详细看流程图)
CachingExecutor#query
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) {
//已经开启了二级缓存,走if里面逻辑
flushCacheIfRequired(ms);
if (ms.isUseCache() && resultHandler == null) {
ensureNoOutParams(ms, boundSql);
@SuppressWarnings("unchecked")
//逐级调用,最后从PerpetualCache获取
List<E> list = (List<E>) tcm.getObject(cache, key);
if (list == null) {
//二级缓存中没有,调用一级缓存(如果没有从数据库中获取)
list = delegate.<E> query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
//存在TransactionalCache中entriesToAddOnCommit.put(key, object);
tcm.putObject(cache, key, list); // issue #578 and #116
}
return list;
}
}
//二级缓存没有开启,走一级缓存
return delegate.<E> query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
}
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()) {
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;
}
BaseExecutor#queryFromDatabase
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;
}
下面我们来看一下在什么地方存储二级缓存TransactionalCache#commit
public void commit() {
if (clearOnCommit) {
delegate.clear();
}
flushPendingEntries();
reset();
}
private void flushPendingEntries() {
for (Map.Entry<Object, Object> entry : entriesToAddOnCommit.entrySet()) {
//逐级调用,保存到PerpetualCache中
delegate.putObject(entry.getKey(), entry.getValue());
}
for (Object entry : entriesMissedInCache) {
if (!entriesToAddOnCommit.containsKey(entry)) {
delegate.putObject(entry, null);
}
}
}
二级缓存清除
CachingExecutor#update执行增删改清理缓存
@Override
public int update(MappedStatement ms, Object parameterObject) throws SQLException {
flushCacheIfRequired(ms);
return delegate.update(ms, parameterObject);
}
private void flushCacheIfRequired(MappedStatement ms) {
Cache cache = ms.getCache();
if (cache != null && ms.isFlushCacheRequired()) {
tcm.clear(cache);
}
}
public void clear(Cache cache) {
getTransactionalCache(cache).clear();
}
同一会话一级缓存清除
BaseExecutor#update
@Override
public int update(MappedStatement ms, Object parameter) throws SQLException {
ErrorContext.instance().resource(ms.getResource()).activity("executing an update").object(ms.getId());
if (closed) {
throw new ExecutorException("Executor was closed.");
}
clearLocalCache();
return doUpdate(ms, parameter);
}
@Override
public void clearLocalCache() {
if (!closed) {
localCache.clear();
localOutputParameterCache.clear();
}
}
三、结束,对于这一块代码,只要你认真跑一遍,你会发现并不难。重点:一级缓存是在创建SqlSession过程中创建的,生命周期为一次请求。二级缓存是在创建SqlSessionFactory过程中创建的,生命周期是应用结束。