- 一级缓存
- 二级缓存
- 总结
对于一名程序员,缓存真的很重要,而且缓存真的是老生常谈的一个话题拉。因为它在我们的开发过程中真的是无处不在。今天LZ带大家来看一下。Mybatis是怎么实现一级缓存和二级缓存的。(自带的缓存机制)
存在BaseExecutor中,是全局的缓存,每次查询后将值存入BaseExecutor的localCache中。key是由ms,parameter,rowBounds和boundSql一起生成的一个值。value就是查询出来的结果。一旦有任何更新变动,就删除整个localCache。
@Override
//生成一级缓存的key的函数,有兴趣的看看,LZ不详细解释了,不是重点。
public CacheKey createCacheKey(MappedStatement ms, Object parameterObject, RowBounds rowBounds, BoundSql boundSql) {
if (closed) {
throw new ExecutorException("Executor was closed.");
}
CacheKey cacheKey = new CacheKey();
cacheKey.update(ms.getId());
cacheKey.update(rowBounds.getOffset());
cacheKey.update(rowBounds.getLimit());
cacheKey.update(boundSql.getSql());
List 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())) {
value = parameterObject;
} else {
MetaObject metaObject = configuration.newMetaObject(parameterObject);
value = metaObject.getValue(propertyName);
}
cacheKey.update(value);
}
}
if (configuration.getEnvironment() != null) {
// issue #176
cacheKey.update(configuration.getEnvironment().getId());
}
return cacheKey;
}
//查询数据库并存入一级缓存的语句
private List queryFromDatabase(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {
List 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
localOutputParameterCache.putObject(key, parameter);
}
return list;
}
@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();
}
}
所以一级缓存的作用级别是SESSION级别的,因为一个session中存放一个Executor。而一级缓存放在Executor。
如果开启了二级缓存的话,你的Executor将会被装饰成CachingExecutor,二级缓存是MapperStatement级的缓存,也就是一个namespace就会有一个缓存,缓存是通过CachingExecutor来操作的。查询出来的结果会存在statement中的cache中,若有更新,删除类的操作默认就会清空该MapperStatement的cache(也可以通过修改xml中的属性,让它不执行),不会影响其他的MapperStatement
@Override
public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql)
throws SQLException {
//获得该MappedStatement的cache
Cache cache = ms.getCache();
if (cache != null) {
//看是否需要清除cache(在xml中可以配置flushCache属性决定何时清空cache)
flushCacheIfRequired(ms);
if (ms.isUseCache() && resultHandler == null) {
//若开启了cache且resultHandler 为空
ensureNoOutParams(ms, parameterObject, boundSql);
@SuppressWarnings("unchecked")
//从TransactionalCacheManager中取cache
List<E> list = (List<E>) tcm.getObject(cache, key);
//若没值
if (list == null) {
//访问数据库
list = delegate.<E> query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
//存入cache
tcm.putObject(cache, key, list); // issue #578 and #116
}
return list;
}
}
return delegate.<E> query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
}
@Override
public int update(MappedStatement ms, Object parameterObject) throws SQLException {
//看是否需要清除cache(在xml中可以配置flushCache属性决定何时清空cache)
flushCacheIfRequired(ms);
return delegate.update(ms, parameterObject);
}
private void flushCacheIfRequired(MappedStatement ms) {
//获得cache
Cache cache = ms.getCache();
if (cache != null && ms.isFlushCacheRequired()) {
//若需要清除,则清除cache
tcm.clear(cache);
}
}
因同一个namespace下的MappedStatement的cache是同一个,而TransactionalCacheManager中统一管理cache是里面的属性transactionalCaches,该属性以MappedStatement中的Cache为key,TransactionalCache对象为Value。即一个namespace对应一个TransactionalCache。
public class TransactionalCacheManager {
private Map transactionalCaches = new HashMap();
……
}
public class TransactionalCache implements Cache {
private static final Log log = LogFactory.getLog(TransactionalCache.class);
//就是对应的namespace中的cache
private Cache delegate;
//提交的时候清除cache的标志位
private boolean clearOnCommit;
//待提交的集合
private Map
一级缓存是sqlSession级别的缓存,存放在BaseExecutor中的localCache中。查询就将结果缓存进去,一旦有更新,删除,插入类的操作就清空缓存。不同的sqlSession之间的缓存是互相不影响的。
二级缓存是namespace级别的,可以理解为一个mapper.xml文件对应一个二级缓存(不同的sqlSession之间的缓存是共享的),然后对缓存的操作是在.xml文件中的标签文件进行控制的。比如下面的代码
<mapper namespace="mapper.TDemoMapper">
<resultMap id="baseMap" type="entity.TDemo">
<result property="id" column="id" jdbcType="INTEGER">result>
<result property="name" column="name" jdbcType="VARCHAR">result>
resultMap>
<select id="getAll" resultType="entity.TDemo" useCache="true" flushCache="false" >
select * from t_demo
select>
mapper>