CachingExcuxtor:
- 装饰器设计模式:缓存装饰器类,对真正的执行器Exexutor进行装饰实现缓存作用;
- 属性Executor delegate:委托对象,存储真正操作数据库的Executor执行器对象;
- 属性TransactionalCacheManager:管理CachingExecutor中使用二级缓存的对象TransactionalCache;
- TransactionalCache:实现Cache类,装饰了二级缓存对象;事务中需要进行二级缓存的各类数据,缓存数据在事务提交后才会真正存入二级缓存中;或事务回退后被丢弃,对缓存没有影响。
- 大部分更新、关闭等方法都是调用委托对象对应的方法delegate.xxx();重点装饰query()方法来使用二级缓存查询数据。
Mybatis一级缓存(即本地缓存 local cache):
- 基于org.apache.ibatis.cache.impl.PerpetualCache类实现,内也是使用Map<Object, Object> cache = new HashMap<>()本地HashMap缓存,存储的作用域为Session会话,会缓存一个会话中执行的所有查询,通过SqlSession.openSesion()开启一个会话就会有一个一级缓存。
- mybatis-config.xml全局配置:默认全局开启,可通过settings标签显示设置localCacheScope,<setting name="localCacheScope" value="SESSION | STATEMENT">;
- 本地缓存实现类PerpetualCache实现了接口Cache;
- 本地缓存的使用源码可以查看BaseExecutor.query()方法 localCache.getObject(key)。
- Scope设置为Statement将不使用缓存:BaseExecutor.query()方法 configuration.getLocalCacheScope() == LocalCacheScope.STATEMENT会clearLocalCache();
- 本地缓存的生命周期和会话的周期是一样的:因为DefaultSqlSession.close()方法调用了executor.close(),BaseExecutor里的close也调用了clearLocalCache方法清除本地缓存。
Mybatis二级缓存(second level cache):
- mybatis-config.xml全局开关:默认开启全局二级缓存开关,通过settings标签,可以显示的设置<setting name="cacheEnabled" value="true"/>,全局性的开启或关闭所有映射器Mapper配置文件中已配置的任何缓存。
- Mapper.xml配置使用二级缓存:二级缓存是基于namespace级别的,所以一个命名空间对应一个二级缓存,如果需要在当前Mapper.xml中配置<cache />标签,也可以通过标签的属性自定义一些缓存控制参数,或者通过修改默认属性type="org.apache.ibatis.cache.impl.PerpetualCache"来指定自己的类实现Cache接口来自定义缓存。
- Select标签:默认属性useCache="true" flushCahce="false",userCache表示是否使用二级缓存(false为不使用,每次直接从数据库中获取), flushCahce为语句被调用时,是否清空一级缓存和二级缓存;
- insert、update、delete更新标签语句:只有默认属性flushCahce="true",表示语句被调用时都会去清空一级缓存和二级缓存,如果不执行刷新缓存会出现脏读;
- 二级缓存的使用源码可以查看CachingExecutor.query()方法 tcm.getObject(cache, key);
- 所有数据都会先放在一级缓存(本地缓存)中,只有当绘画提交或关闭时才会提交到二级缓存中。
- 二级缓存保证查询的时候必须唯一,往往对象的key可以使用String对象,而二级缓存通过生成一个复杂的CacheKey来做缓存项,key的构成命名空间、方法名、翻页偏移量、SQL语句、参数、数据源环境等,由特定的hashCode计算方法和内部属性来确定是否时同一个Cachekey。
二级缓存的流程分析:
- 在Mapper.xml标签中配置<cache />
- XMLMapperBuilder.configurationElement()方法便利解析各个元素节点;
- cacheElement(context.evalNode("cache"));解析cache配置,获取各个属性,调用builderAssistant.useNewCache();
- MapperBuilderAssistant.useNewCache()方法会实例化Cache当前Mapper.xml的二级缓存对象,并通过configuration.addCache(cache);方法添加到全局配置信息的Map<String, Cache> caches中来存储二级缓存对象。
- CachingExecutor.query()方法执行查询操作时,通过Cache cache = ms.getCache()获取二级缓存对象
- 通过tcm.getObject(cache, key)获取二级缓存对象cache,指定的CacheKey的结果。
默认开启缓存时,SQL执行器会使用CachingExcuxtor缓存装饰类去装饰真正的执行器(BatchExecutor、ReuseExecutor、SimpleExecutor)。所以执行executor.query()会经过CachingExcuxtor装饰类的二级缓存逻辑;
// CachingExecutor 源码
package org.apache.ibatis.executor;
public class CachingExecutor implements Executor {
// 委托对象,存储真正操作数据库的Executor执行器对象
private final Executor delegate;
// TransactionalCacheManager 管理CachingExecutor中使用二级缓存的对象TransactionalCache
// TransactionalCache 实现Cache类,封装了二级缓存对象
private final TransactionalCacheManager tcm = new TransactionalCacheManager();
public CachingExecutor(Executor delegate) {
// 设置委托对象
this.delegate = delegate;
// SQL执行器中添加设置缓存装饰器
delegate.setExecutorWrapper(this);
}
@Override
public Transaction getTransaction() {
// 获取委托对象的事务对象
return delegate.getTransaction();
}
@Override
public void close(boolean forceRollback) {
try {
// issues #499, #524 and #573
// tcm事务缓存管理对象关闭
if (forceRollback) {
tcm.rollback();
} else {
tcm.commit();
}
} finally {
// 委托对象关闭
delegate.close(forceRollback);
}
}
@Override
public boolean isClosed() {
return delegate.isClosed();
}
@Override
public int update(MappedStatement ms, Object parameterObject) throws SQLException {
// 更新前清理缓存
flushCacheIfRequired(ms);
// 调用真正的执行器的update方法更新数据
return delegate.update(ms, parameterObject);
}
@Override
public <E> Cursor<E> queryCursor(MappedStatement ms, Object parameter, RowBounds rowBounds) throws SQLException {
// 更新前清理缓存
flushCacheIfRequired(ms);
// 调用真正的执行器的queryCursor方法查询游标
return delegate.queryCursor(ms, parameter, rowBounds);
}
@Override
public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {
// 获取BoundSql对象
BoundSql boundSql = ms.getBoundSql(parameterObject);
// 创建查询语句对应的Cachekey对象
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 {
// 检查是否开启了二级缓存,即是否存在对应Mapper.xml的二级缓存对象
Cache cache = ms.getCache();
if (cache != null) {
// 判断是否清空二级缓存
// 通过Mapper.xml的SQL语句标签上的flushCache="true" || 或!select标签
flushCacheIfRequired(ms);
// isUseCache() = (Mapper.xml的SQL语句标签上的useCache="true" || select标签) ;true为使用缓存
if (ms.isUseCache() && resultHandler == null) {
// 存储过程语句时校验参数是否包含输入类型的参数
ensureNoOutParams(ms, boundSql);
@SuppressWarnings("unchecked")
// 通过二级缓存对象Cache,取出对应CacheKey的缓存数据
List<E> list = (List<E>) tcm.getObject(cache, key);
if (list == null) {
// 二级缓存中没有查到结果则使用委托对象来执行SQL查询
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);
}
@Override
public List<BatchResult> flushStatements() throws SQLException {
return delegate.flushStatements();
}
@Override
public void commit(boolean required) throws SQLException {
delegate.commit(required);
tcm.commit();
}
@Override
public void rollback(boolean required) throws SQLException {
try {
delegate.rollback(required);
} finally {
if (required) {
tcm.rollback();
}
}
}
private void ensureNoOutParams(MappedStatement ms, BoundSql boundSql) {
if (ms.getStatementType() == StatementType.CALLABLE) {
for (ParameterMapping parameterMapping : boundSql.getParameterMappings()) {
if (parameterMapping.getMode() != ParameterMode.IN) {
throw new ExecutorException("Caching stored procedures with OUT params is not supported. Please configure useCache=false in " + ms.getId() + " statement.");
}
}
}
}
@Override
public CacheKey createCacheKey(MappedStatement ms, Object parameterObject, RowBounds rowBounds, BoundSql boundSql) {
return delegate.createCacheKey(ms, parameterObject, rowBounds, boundSql);
}
@Override
public boolean isCached(MappedStatement ms, CacheKey key) {
return delegate.isCached(ms, key);
}
@Override
public void deferLoad(MappedStatement ms, MetaObject resultObject, String property, CacheKey key, Class<?> targetType) {
delegate.deferLoad(ms, resultObject, property, key, targetType);
}
@Override
public void clearLocalCache() {
delegate.clearLocalCache();
}
private void flushCacheIfRequired(MappedStatement ms) {
Cache cache = ms.getCache();
if (cache != null && ms.isFlushCacheRequired()) {
tcm.clear(cache);
}
}
@Override
public void setExecutorWrapper(Executor executor) {
throw new UnsupportedOperationException("This method should not be called");
}
}
当没有拿到缓存时,会调用委托对象的查询方法delegate.query();拿简单执行器SimpleExecutor的executor.query()来分析,SimpleExecutor继承于BaseExecutor,query方法的实现在BaseExecutor类中。在BaseExecutor也进行一级缓存拿取的逻辑,如果没有缓存则才会从数据库中查询list = queryFromDatabase(ms, parameter, rowBounds, resultHandler, key, boundSql);
// BaseExecutor 源码
package org.apache.ibatis.executor;
public abstract class BaseExecutor implements Executor {
@Override
public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {
// 从MappedStatement对象中根据参数获取BoundSql对象
BoundSql boundSql = ms.getBoundSql(parameter);
// 生成缓存的唯一key
CacheKey key = createCacheKey(ms, parameter, rowBounds, boundSql);
return query(ms, parameter, rowBounds, resultHandler, key, boundSql);
}
@SuppressWarnings("unchecked")
@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.");
}
// 查询调用栈为0,且Mapper.xml的SQL语句标签上的flushCache="true" || !select标签
// 则清空一级缓存,清空输出参数缓存
if (queryStack == 0 && ms.isFlushCacheRequired()) {
clearLocalCache();
}
List<E> list;
try {
// 查询调用栈++
queryStack++;
// 结果处理器为null时,尝试从一级缓存获取已缓存结果
list = resultHandler == null ? (List<E>) localCache.getObject(key) : null;
if (list != null) {
// 如果查到localCache缓存,处理输出参数缓存
handleLocallyCachedOutputParameters(ms, key, parameter, boundSql);
} else {
// 没有缓存结果,则从数据库查询结果
list = queryFromDatabase(ms, parameter, rowBounds, resultHandler, key, boundSql);
}
} finally {
queryStack--;
}
if (queryStack == 0) {
// 延迟加载集合,遍历加载数据到list中
for (DeferredLoad deferredLoad : deferredLoads) {
// 加载延迟加载类
deferredLoad.load();
}
// issue #601
// 延迟加载集合清空
deferredLoads.clear();
// "localCacheScope"一级缓存存储作用域 value="STATEMENT"
if (configuration.getLocalCacheScope() == LocalCacheScope.STATEMENT) {
// issue #482
// 则清空一级缓存,不做存储
clearLocalCache();
}
}
return list;
}
// 抽象的方法,需要具体的执行器来实现逻辑,模版方法模式
protected abstract <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql)
throws SQLException;
// 从数据中查询数据
private <E> List<E> queryFromDatabase(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {
List<E> list;
// 一级缓存,使用key添加一个一级缓存占位符
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;
}
// 创建缓存项key
@Override
public CacheKey createCacheKey(MappedStatement ms, Object parameterObject, RowBounds rowBounds, BoundSql boundSql) {
// 如果执行器关闭则报错
if (closed) {
throw new ExecutorException("Executor was closed.");
}
CacheKey cacheKey = new CacheKey();
// MappedStatement的id,即namespace+id
cacheKey.update(ms.getId());
// sql分页参数的偏移量
cacheKey.update(rowBounds.getOffset());
// sql分页参数的限制条数
cacheKey.update(rowBounds.getLimit());
// SQL语句本身
cacheKey.update(boundSql.getSql());
// 传给JDBC的参数
List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
TypeHandlerRegistry typeHandlerRegistry = ms.getConfiguration().getTypeHandlerRegistry();
// mimic DefaultParameterHandler logic
for (ParameterMapping parameterMapping : parameterMappings) {
if (parameterMapping.getMode() != ParameterMode.OUT) {
Object value;
// 获取对应映射名
String propertyName = parameterMapping.getProperty();
if (boundSql.hasAdditionalParameter(propertyName)) {
// 获取额外映射值
value = boundSql.getAdditionalParameter(propertyName);
} else if (parameterObject == null) {
value = null;
} else if (typeHandlerRegistry.hasTypeHandler(parameterObject.getClass())) {
// 如果存在parameterObject对应类型处理器,value即为parameterObject
value = parameterObject;
} else {
// 从元对象中获取propertyName的值
MetaObject metaObject = configuration.newMetaObject(parameterObject);
value = metaObject.getValue(propertyName);
}
// 添加参数值
cacheKey.update(value);
}
}
if (configuration.getEnvironment() != null) {
// issue #176
// 获取环境id,当配置多个环境时,需要通过环境id区分
cacheKey.update(configuration.getEnvironment().getId());
}
return cacheKey;
}
}
TransactionalCache(二级缓存的事务缓存类):
// TransactionalCache 源码
package org.apache.ibatis.cache.decorators;
// 事务中需要进行二级缓存的各类数据
// 缓存数据在事务提交后才会真正存入二级缓存中;或事务回退后被丢弃,对缓存没有影响
public class TransactionalCache implements Cache {
private static final Log log = LogFactory.getLog(TransactionalCache.class);
// 二级缓存缓存委托对象,真正存放缓存数据的二级缓存对象
private final Cache delegate;
// 是否在事务提交时清空二级缓存标识,
// 为true时也不可以查询当前TransactionalCache的缓存数据
private boolean clearOnCommit;
// 需要在事务提交后存储到二级缓存中的数据Map,缓存项key和缓存值value的Map
// 在事务提交时,会将其中的数据添加到二级缓存中
private final Map<Object, Object> entriesToAddOnCommit;
// 未命中的缓存key记录,事务提交时,也会放入二级缓存中(key,null)
private final Set<Object> entriesMissedInCache;
public TransactionalCache(Cache delegate) {
this.delegate = delegate;
this.clearOnCommit = false;
this.entriesToAddOnCommit = new HashMap<>();
this.entriesMissedInCache = new HashSet<>();
}
@Override
public String getId() {
return delegate.getId();
}
@Override
public int getSize() {
return delegate.getSize();
}
// 查询二级缓存的缓存数据
@Override
public Object getObject(Object key) {
// issue #116
// 直接查询缓存结果
Object object = delegate.getObject(key);
if (object == null) {
// 没查到缓存数据时,将CacheKey添加到未命中的缓存key记录中
entriesMissedInCache.add(key);
}
// issue #146
// 为true则不返回缓存数据
if (clearOnCommit) {
return null;
} else {
return object;
}
}
@Override
public void putObject(Object key, Object object) {
// 记录需要放入缓存的key,value
entriesToAddOnCommit.put(key, object);
}
@Override
public Object removeObject(Object key) {
return null;
}
@Override
// 清空
public void clear() {
clearOnCommit = true;
// 清空entriesToAddOnCommit记录的缓存
entriesToAddOnCommit.clear();
}
// 事务提交
public void commit() {
if (clearOnCommit) {
// 清空二级缓存
delegate.clear();
}
// 将entriesToAddOnCommit记录的缓存key和value刷新到二级缓存中
flushPendingEntries();
reset();
}
// 事务回滚
public void rollback() {
unlockMissedEntries();
reset();
}
// 重置标识
// 清空entriesToAddOnCommit集合和entriesMissedlnCache集合
private void reset() {
clearOnCommit = false;
entriesToAddOnCommit.clear();
entriesMissedInCache.clear();
}
private void flushPendingEntries() {
// 将entriesToAddOnCommit记录的缓存key和value刷新到二级缓存中
for (Map.Entry<Object, Object> entry : entriesToAddOnCommit.entrySet()) {
delegate.putObject(entry.getKey(), entry.getValue());
}
// 将entriesMissedlnCache集合中记录的缓存项刷新到二级缓存中
for (Object entry : entriesMissedInCache) {
if (!entriesToAddOnCommit.containsKey(entry)) {
delegate.putObject(entry, null);
}
}
}
// 事务回滚时调用,解锁丢失的缓存数据
// entriesMissedlnCache集合中记录的缓存项从二级缓存中删除
private void unlockMissedEntries() {
for (Object entry : entriesMissedInCache) {
try {
delegate.removeObject(entry);
} catch (Exception e) {
log.warn("Unexpected exception while notifying a rollback to the cache adapter. "
+ "Consider upgrading your cache adapter to the latest version. Cause: " + e);
}
}
}
}
// TransactionalCacheManager 源码
package org.apache.ibatis.cache;
// 管理 CachingExecutor中使用的二级缓存对象TransactionalCache
public class TransactionalCacheManager {
// 对应的二级缓存对象
private final Map<Cache, TransactionalCache> transactionalCaches = new HashMap<>();
public void clear(Cache cache) {
// 清空二级缓存
getTransactionalCache(cache).clear();
}
// 获取二级缓存对象中指定key的缓存结果
public Object getObject(Cache cache, CacheKey key) {
return getTransactionalCache(cache).getObject(key);
}
// 添加指定Mapper的Cache二级缓存对象,缓存key和value
public void putObject(Cache cache, CacheKey key, Object value) {
getTransactionalCache(cache).putObject(key, value);
}
// 事务提交
public void commit() {
for (TransactionalCache txCache : transactionalCaches.values()) {
txCache.commit();
}
}
// 回滚
public void rollback() {
for (TransactionalCache txCache : transactionalCaches.values()) {
txCache.rollback();
}
}
// 获取二级缓存对象
private TransactionalCache getTransactionalCache(Cache cache) {
return MapUtil.computeIfAbsent(transactionalCaches, cache, TransactionalCache::new);
}
}
Mybatis缓存的Key的设计:通过自定义的CacheKey类
// CacheKey 源码
package org.apache.ibatis.cache;
// 缓存项key,来确定一个缓存项
public class CacheKey implements Cloneable, Serializable {
private static final long serialVersionUID = 1146682552656046210L;
// 定义一个空的缓存项,空缓存项不能进行更新
public static final CacheKey NULL_CACHE_KEY = new CacheKey() {
@Override
public void update(Object object) {
throw new CacheException("Not allowed to update a null cache key instance.");
}
@Override
public void updateAll(Object[] objects) {
throw new CacheException("Not allowed to update a null cache key instance.");
}
};
// 乘子,用来作为hashCode计算的乘数
// 通常使用的备选乘数:31, 33, 37, 39 和 41
private static final int DEFAULT_MULTIPLIER = 37;
// hashCode值,默认17,hashcode初始质子数17
// 如果使用较小质子数,那么乘积范围较小,容易造成哈希值的冲突
// 如果使用较大质子数,得到的hashCode值会超过int范围
private static final int DEFAULT_HASHCODE = 17;
// 乘子,用来作为hashCode计算的乘数
private final int multiplier;
// hashCode值
private int hashcode;
// 校验和,hash值的和
private long checksum;
// pdateList的中元素个数
private int count;
// 8/21/2017 - Sonarlint flags this as needing to be marked transient. While true if content is not serializable, this
// is not always true and thus should not be marked transient.
// 存储namepace.id 、offerset、limit、sql、params等
private List<Object> updateList;
// 构造初始化
public CacheKey() {
this.hashcode = DEFAULT_HASHCODE;
this.multiplier = DEFAULT_MULTIPLIER;
this.count = 0;
this.updateList = new ArrayList<>();
}
public CacheKey(Object[] objects) {
this();
updateAll(objects);
}
public int getUpdateCount() {
return updateList.size();
}
public void update(Object object) {
// 获取object的hash值
int baseHashCode = object == null ? 1 : ArrayUtil.hashCode(object);
// 更新count、checksum以及hashcode的值
count++;
checksum += baseHashCode;
baseHashCode *= count;
// hashcode更新的方法
// newHashcode = oldHashcode * 37 + baseHashCode
hashcode = multiplier * hashcode + baseHashCode;
// 将对象添加到updateList中
updateList.add(object);
}
// 更新数组对象
public void updateAll(Object[] objects) {
for (Object o : objects) {
// 调用单个更新方法
update(o);
}
}
// 重载对象是否相同判断方法
@Override
public boolean equals(Object object) {
// 判断是否为同一个对象
if (this == object) {
return true;
}
// 对象不是CacheKey的实例直接返回false
if (!(object instanceof CacheKey)) {
return false;
}
// 对象转为CacheKey类型
final CacheKey cacheKey = (CacheKey) object;
// 判断hashcode是否相同
if (hashcode != cacheKey.hashcode) {
return false;
}
// 判断checksum是否相同
if (checksum != cacheKey.checksum) {
return false;
}
// 判断updateList数量是否相同
if (count != cacheKey.count) {
return false;
}
// 以上都相同,通过遍历updateList元素中是否相同
for (int i = 0; i < updateList.size(); i++) {
Object thisObject = updateList.get(i);
Object thatObject = cacheKey.updateList.get(i);
if (!ArrayUtil.equals(thisObject, thatObject)) {
return false;
}
}
return true;
}
@Override
public int hashCode() {
return hashcode;
}
@Override
public String toString() {
StringJoiner returnValue = new StringJoiner(":");
returnValue.add(String.valueOf(hashcode));
returnValue.add(String.valueOf(checksum));
updateList.stream().map(ArrayUtil::toString).forEach(returnValue::add);
return returnValue.toString();
}
@Override
public CacheKey clone() throws CloneNotSupportedException {
// 克隆CacheKey对象
CacheKey clonedCacheKey = (CacheKey) super.clone();
// 复制数组重新赋值
clonedCacheKey.updateList = new ArrayList<>(updateList);
return clonedCacheKey;
}
}
Mybatis缓存的设计,通过Cache接口实现类:
- PerpetualCache:永久缓存,只有这唯一一个基础实现(org.apache.ibatis.cache.impl),其他实现类全都是装饰模式(org.apache.ibatis.cache.decorators)持有另一个缓存对象;
- PerpetualCache里面就是维护着一个本地的HashMap,所以不支持多线程,因为没能实现接口的锁功能,而且内部的map不是并发的map;
- FifoCache(先进先出缓存策略的缓存装饰类)
- LruCache(最近最少使用的缓存策略的缓存装饰类)
- SoftCache(软引用回收策略缓存装饰类)
- WeakCache(弱引用回收策略缓存装饰类)
- LoggingCache(日志功能装饰类,用于记录缓存的命中率)
- ScheduledCache(定时清空Cache类)
- SerializedCache(序列化功能,将值序列化后存到缓存中)
- SynchronizedCache(同步的缓存Cache类)
- TransactionalCache(二级缓存的事务缓存类)
// PerpetualCache 源码
package org.apache.ibatis.cache.impl;
public class PerpetualCache implements Cache {
// 缓冲对象的唯一标识,namespace.id
private final String id;
// 通过HashMap存储
private final Map<Object, Object> cache = new HashMap<>();
public PerpetualCache(String id) {
this.id = id;
}
@Override
public String getId() {
return id;
}
@Override
public int getSize() {
return cache.size();
}
@Override
public void putObject(Object key, Object value) {
cache.put(key, value);
}
@Override
public Object getObject(Object key) {
return cache.get(key);
}
@Override
public Object removeObject(Object key) {
return cache.remove(key);
}
@Override
public void clear() {
cache.clear();
}
@Override
public boolean equals(Object o) {
// id不存在报错
if (getId() == null) {
throw new CacheException("Cache instances require an ID.");
}
// 对象判断是否相等,即存储地址判断
if (this == o) {
return true;
}
// 是否是子类
if (!(o instanceof Cache)) {
return false;
}
Cache otherCache = (Cache) o;
// 通过判断ID相等
return getId().equals(otherCache.getId());
}
@Override
public int hashCode() {
if (getId() == null) {
throw new CacheException("Cache instances require an ID.");
}
// 用属性ID的hashCode作为对象的hashCode
return getId().hashCode();
}
}