为了能提高系统性能,缓存必不可少,mybatis同样也是有缓存的!
分一级缓存和二级缓存,该篇介绍下一级缓存!!
我们回顾下配置阶段,再创建executor执行器时,有个Configuration.newExecutor方法,来看下
public Executor newExecutor(Transaction transaction, ExecutorType executorType) {
executorType = executorType == null ? defaultExecutorType : executorType;
executorType = executorType == null ? ExecutorType.SIMPLE : executorType;
Executor executor;
if (ExecutorType.BATCH == executorType) {
executor = new BatchExecutor(this, transaction);
} else if (ExecutorType.REUSE == executorType) {
executor = new ReuseExecutor(this, transaction);
} else {
executor = new SimpleExecutor(this, transaction);
}
//默认cacheEnabled为true,那么此时的执行器其实是CachingExecutor,将executor当参数传递给CachingExecutor,装饰了下,增加了缓存功能
if (cacheEnabled) {
executor = new CachingExecutor(executor);
}
executor = (Executor) interceptorChain.pluginAll(executor);
return executor;
}
那么我们再执行Executor.query的时候,也就是说先调用CachingExecutor的query方法
public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {
BoundSql boundSql = ms.getBoundSql(parameterObject);
//cachekey 即缓存的标识
CacheKey key = createCacheKey(ms, parameterObject, rowBounds, boundSql);
return query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
}
public CacheKey createCacheKey(MappedStatement ms, Object parameterObject, RowBounds rowBounds, BoundSql boundSql) {
if (closed) {
throw new ExecutorException("Executor was closed.");
}
CacheKey cacheKey = new CacheKey();
//StatementId
cacheKey.update(ms.getId());
//自带分页参数数据
cacheKey.update(rowBounds.getOffset());
cacheKey.update(rowBounds.getLimit());
//sql语句
cacheKey.update(boundSql.getSql());
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())) {
value = parameterObject;
} else {
MetaObject metaObject = configuration.newMetaObject(parameterObject);
value = metaObject.getValue(propertyName);
}
cacheKey.update(value);
}
}
if (configuration.getEnvironment() != null) {
// issue #176
//xml中environment的id cacheKey.update(configuration.getEnvironment().getId());
}
//以上种种,组成一个缓存标识
return cacheKey;
}
public void update(Object object) {
//生成hashcode,具体算法大家可以进去看,主要是分集合和非集合之分
int baseHashCode = object == null ? 1 : ArrayUtil.hashCode(object);
count++;
checksum += baseHashCode;
//扩大count倍
baseHashCode *= count;
//加上扩展固定值
hashcode = multiplier * hashcode + baseHashCode;
updateList.add(object);
}
好了 有了key,那么就应该从缓存中获取数据了,
public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {
...省略
//获取缓存
list = resultHandler == null ? (List<E>) localCache.getObject(key) : null;
...判断是否为空
//为空,则从数据库中获取
list = queryFromDatabase(ms, parameter, rowBounds, resultHandler, key, boundSql);
...省略
return list;
}
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 {
//每次都会移除 PerpetualCache 其实就是放到hashmap中
localCache.removeObject(key);
}
//在put进缓存
localCache.putObject(key, list);
if (ms.getStatementType() == StatementType.CALLABLE) {
localOutputParameterCache.putObject(key, parameter);
}
return list;
}
好了 ,缓存是不是很简单,原来就是放到PerpetualCache类的hashmap中。
最后 session.close();关闭时,将localCache = null;缓存设置为null!!
也就是说 一级缓存的作用体现在同一个session中,一旦关闭,则清空,适合同一个session频繁的调用同样的sql(我想这种情况很少吧!)
而且,这种缓存的做法,默认状态是开启的,自动进行缓存,如果session长时间不关的话,会造成数据量越来越大,造成内存溢出!所以要及时关闭session哦!