mybatis缓存
一级缓存
一级缓存是对于同一个SqlSession
。
mybatis默认开启一级缓存, SqlSession
将查询任务委托给执行器Executor
,执行器Executor
中的PerpetualCache
对象用来存放一级缓存数据。
PerpetualCache
底层采用HashMap
实现,key是查询的标识,value即是查询结果。
一次查询操作中:生成此次查询的标识key,根据key获取value即一级缓存数据,如果不为空,则直接返回缓存数据;如果为空,则从数据库中获取数据,并将数据放到PerpetualCache
中。
1、PerpetualCache
的key:
在类BaseExecutor
中,实现了接口Executor
中的createCacheKey
方法:
public CacheKey createCacheKey(MappedStatement ms, Object parameterObject, RowBounds rowBounds, BoundSql boundSql) {
......
CacheKey cacheKey = new CacheKey();
cacheKey.update(ms.getId());
cacheKey.update(rowBounds.getOffset());
cacheKey.update(rowBounds.getLimit());
cacheKey.update(boundSql.getSql());
......
// value指的是处理过的查询参数
cacheKey.update(value);
......
// Environment是mybatis的运行环境,可以为mybatis配置多个运行环境,根据Environment的Id进行区分:https://blog.csdn.net/u014268482/article/details/80595602
if (this.configuration.getEnvironment() != null) {
cacheKey.update(this.configuration.getEnvironment().getId());
}
return cacheKey;
}
由此,我们可以看出,mybatis根据下列信息生成key:
- MappedStatement的Id
- 查询范围
- sql
- 参数
- mybatis的运行环境id
2、一级缓存生命周期
- 开启会话,创建一个新的
SqlSession
对象,SqlSession
对象中有一个Executor
对象,Executor
对象中有一个PerpetualCache
对象。 - 会话结束或
SqlSession
调用close()
方法,释放SqlSession
对象,同时释放Executor
对象、PerpetualCache
对象。 SqlSession
调用clearCache()
方法或执行了update()、delete()、insert()方法,清空PerpetualCache
对象,但是还可以再次使用。
3、一级缓存和spring事务的关系
一般来说,每次查询后,一级缓存就会被清空掉。但是如果加上了spring事务,每次查询后,一级缓存不会被清空掉,当事务结束或者有新增、更新、删除操作时,一级缓存会被清空。
二级缓存
二级缓存是对于同一个map接口。
springboot开启二级缓存配置
1、配置文件开启mybatis二级缓存,默认开启
mybatis:
configuration:
cache-enabled: true
2、在mapper.xml中加入如下代码
<cache/>
MappedStatement
中有一个Cache
属性,如果mapper.xml中没有<cache/>
,则此属性为null;如果在mapper.xml中加入<cache/>
后,此mapper.xml的二级缓存相关信息会封装到Cache
属性中,Cache
的id
就是mapper.xml的namespace,即map接口全路径名。
如果多个mapper.xml对应同一个map接口,那么这些mapper.xml对应的MappedStatement
中的Cache
属性是一样的。
原理
二级缓存采用装饰者模式,当开启二级缓存后,执行器采用CachingExecutor
。
查询逻辑
CachingExecutor
的query
方法:
public class CachingExecutor implements Executor {
private final Executor delegate;
// 事务缓存管理器
private final TransactionalCacheManager tcm = new TransactionalCacheManager();
......
/**
* 查询代码
*
* @param key 和一级缓存生成的key方法一样
* @return 查询结果
*/
public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {
// 1、判断此mapper.xml是否开启了二级缓存
Cache cache = ms.getCache();
if (cache != null) {
......
// 1.1、从二级缓存中获取数据
List<E> list = (List)this.tcm.getObject(cache, key);
if (list == null) {
// 1.1.1、二级缓存中无此数据,走一级缓存查询逻辑
list = this.delegate.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
// 1.1.2、将数据放到二级缓存中
this.tcm.putObject(cache, key, list);
}
return list;
}
// 2、此mapper.xml未开启二级缓存,走一级缓存查询逻辑
return this.delegate.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
}
}
TransactionalCacheManager
:
public class TransactionalCacheManager {
// 事务缓存map
private final Map<Cache, TransactionalCache> transactionalCaches = new HashMap();
public TransactionalCacheManager() {
}
public void clear(Cache cache) {
this.getTransactionalCache(cache).clear();
}
/**
* 获取事务缓存中的缓存
*/
public Object getObject(Cache cache, CacheKey key) {
return this.getTransactionalCache(cache).getObject(key);
}
/**
* 增加事务缓存中的缓存
*/
public void putObject(Cache cache, CacheKey key, Object value) {
this.getTransactionalCache(cache).putObject(key, value);
}
/**
* 提交数据
*/
public void commit() {
Iterator var1 = this.transactionalCaches.values().iterator();
while(var1.hasNext()) {
TransactionalCache txCache = (TransactionalCache)var1.next();
txCache.commit();
}
}
public void rollback() {
Iterator var1 = this.transactionalCaches.values().iterator();
while(var1.hasNext()) {
TransactionalCache txCache = (TransactionalCache)var1.next();
txCache.rollback();
}
}
/**
* 获取事务缓存
*/
private TransactionalCache getTransactionalCache(Cache cache) {
TransactionalCache txCache = (TransactionalCache)this.transactionalCaches.get(cache);
if (txCache == null) {
txCache = new TransactionalCache(cache);
this.transactionalCaches.put(cache, txCache);
}
return txCache;
}
}
TransactionalCache
:
public class TransactionalCache implements Cache {
private static final Log log = LogFactory.getLog(TransactionalCache.class);
// 缓存
private final Cache delegate;
private boolean clearOnCommit;
// 需要提交到缓存中的数据
private final Map<Object, Object> entriesToAddOnCommit;
// 缓存中没有的数据的key集合
private final Set<Object> entriesMissedInCache;
public TransactionalCache(Cache delegate) {
this.delegate = delegate;
this.clearOnCommit = false;
this.entriesToAddOnCommit = new HashMap();
this.entriesMissedInCache = new HashSet();
}
public String getId() {
return this.delegate.getId();
}
public int getSize() {
return this.delegate.getSize();
}
/**
* 获取二级缓存数据
*/
public Object getObject(Object key) {
// 从二级缓存中获取数据
Object object = this.delegate.getObject(key);
if (object == null) {
// 二级缓存中无此数据,将key暂时放到内存中
this.entriesMissedInCache.add(key);
}
return this.clearOnCommit ? null : object;
}
public ReadWriteLock getReadWriteLock() {
return null;
}
/**
* 将数据暂时存到内存中
*/
public void putObject(Object key, Object object) {
this.entriesToAddOnCommit.put(key, object);
}
public Object removeObject(Object key) {
return null;
}
public void clear() {
this.clearOnCommit = true;
this.entriesToAddOnCommit.clear();
}
/**
* 提交数据
*/
public void commit() {
if (this.clearOnCommit) {
this.delegate.clear();
}
this.flushPendingEntries();
this.reset();
}
public void rollback() {
this.unlockMissedEntries();
this.reset();
}
private void reset() {
this.clearOnCommit = false;
this.entriesToAddOnCommit.clear();
this.entriesMissedInCache.clear();
}
/**
* 将内存中的entriesToAddOnCommit、entriesMissedInCache数据推到缓存中
*/
private void flushPendingEntries() {
Iterator var1 = this.entriesToAddOnCommit.entrySet().iterator();
while(var1.hasNext()) {
Entry<Object, Object> entry = (Entry)var1.next();
this.delegate.putObject(entry.getKey(), entry.getValue());
}
var1 = this.entriesMissedInCache.iterator();
while(var1.hasNext()) {
Object entry = var1.next();
if (!this.entriesToAddOnCommit.containsKey(entry)) {
this.delegate.putObject(entry, (Object)null);
}
}
}
private void unlockMissedEntries() {
Iterator var1 = this.entriesMissedInCache.iterator();
while(var1.hasNext()) {
Object entry = var1.next();
try {
this.delegate.removeObject(entry);
} catch (Exception var4) {
log.warn("Unexpected exception while notifiying a rollback to the cache adapter.Consider upgrading your cache adapter to the latest version. Cause: " + var4);
}
}
}
}
当查询结束以后,会执行commit()
将内存中的事务缓存数据推到二级缓存中。
二级缓存缺陷
如果一张表在多个mapper.xml中出现,一个mapper.xml因为更新表刷新了此mapper.xml的二级缓存,此时其他mapper.xml的二级缓存是没有刷新的。