https://blog.csdn.net/2401_83384536/article/details/136707325
配置
public class Configuration {
// 一级缓存,默认开启
protected LocalCacheScope localCacheScope = LocalCacheScope.SESSION;
// 二级缓存,默认开启
protected boolean cacheEnabled = true;
}一级缓存
一级缓存 Mybatis 是默认开启的,是不需要我们去管的,不过一级缓存是 SqlSession 级别的,在操作数据库的时候我们需要创建 SqlSession 对象(使用了 SqlSessionTemplate 来代理了 DefaultSqlSession 执行), SqlSession 中的 executor(BaseExecutor)内部有个 PerpetualCache localCache,PerpetualCache 中使用的是 HashMap 作缓存。
默认配置的 local-cache-scope 为 session,表示开启了。
因为 SqlSessionTemplate 的代理,只有在同一 Spring 事务情况下会使用同一个 SqlSession ,同一个 Executor,同一个缓存,缓存生效。否则创建新的 SqlSession,缓存不生效。
SqlSession 进行 update、commit、close、rollback 都会进行清除缓存(对应是在 BaseExecutor 中进行)。
缓存结构
public abstract class BaseExecutor implements Executor {
protected PerpetualCache localCache;
protected PerpetualCache localOutputParameterCache;
protected Configuration configuration;
public void clearLocalCache() {
if (!closed) {
localCache.clear();
localOutputParameterCache.clear();
}
}
}执行
BaseExecutor
update(..)
clearLocalCache();
return doUpdate(ms, parameter);
query(..)
// 一般不执行
if (queryStack == 0 && ms.isFlushCacheRequired()) {
clearLocalCache();
}
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);
}
queryStack--;
// 如果关闭了一级缓存,则清除缓存
if (configuration.getLocalCacheScope() == LocalCacheScope.STATEMENT) {
clearLocalCache();
}
queryFromDatabase(..)
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);
}
commit()
// 事务提交前清除缓存
clearLocalCache();
flushStatements();
if (required) {
transaction.commit();
}
close()
rollback(forceRollback);
transaction.close();
rollback()
// 事务回滚前清除缓存
clearLocalCache();
flushStatements(true);
if (required) {
transaction.rollback();
}二级缓存
二级缓存是 Mapper 级别的,一个 Mapper 有着一个缓存区,就是说不管几个 SqlSession ,只要调用的是同一个 Mapper 接口(而不是同一个 Mapper 代理实例)中的同一方法,缓存数据就会是共享的,不过二级缓存需要我们自己去开启。
虽然默认配置的 cache-enabled 为 true 了,但还需要在 Mapper上加
@CacheNamespace注解 或者 xml 中加<cache></cache>
Configuration
Executor newExecutor(Transaction transaction, ExecutorType executorType)
...
// 开启的话则使用 CachingExecutor
if (cacheEnabled) {
executor = new CachingExecutor(executor);
}
// 处理拦截器,生成代理
executor = (Executor) interceptorChain.pluginAll(executor);
return executor;缓存结构
public class CachingExecutor implements Executor {
private final Executor delegate;
private final TransactionalCacheManager tcm = new TransactionalCacheManager();
}
public class TransactionalCacheManager {
private final Map<Cache, TransactionalCache> transactionalCaches = new HashMap<>();
// 默认的 key 结构如下:
/*
SynchronizedCache
LoggingCache
SerializedCache
LruCache
PerpetualCache
HashMap
*/
public void clear(Cache cache) {
getTransactionalCache(cache).clear();
}
public void commit() {
for (TransactionalCache txCache : transactionalCaches.values()) {
txCache.commit();
}
}
public void rollback() {
for (TransactionalCache txCache : transactionalCaches.values()) {
txCache.rollback();
}
}
}
public class TransactionalCache implements Cache {
private final Cache delegate;
private boolean clearOnCommit;
private final Map<Object, Object> entriesToAddOnCommit;
private final Set<Object> entriesMissedInCache;
public void clear() {
clearOnCommit = true;
entriesToAddOnCommit.clear();
}
public void commit() {
if (clearOnCommit) {
delegate.clear();
}
flushPendingEntries();
reset();
}
public void rollback() {
unlockMissedEntries();
reset();
}
private void reset() {
clearOnCommit = false;
entriesToAddOnCommit.clear();
entriesMissedInCache.clear();
}
}执行
CachingExecutor
update(..)
// 清空二级缓存
flushCacheIfRequired(..)
return delegate.update(..);
flushCacheIfRequired()
Cache cache = ms.getCache();
// 二级缓存不为空 且 不是查询操作则清空
if (cache != null && ms.isFlushCacheRequired()) {
tcm.clear(cache);
}
query()
Cache cache = ms.getCache();
如果不为空
// 查二级缓存
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);
}
return list;
// 通过 BaseExecutor 去获取,查一级缓存、数据库
return delegate.query(..);
commit()
delegate.commit(required);
tcm.commit();
close()
if (forceRollback) {
tcm.rollback();
} else {
tcm.commit();
}
delegate.close(forceRollback);
rollback()
delegate.rollback(required);
if (required) {
tcm.rollback();
}