这里使用了门面模式,实际的执行的Executor,门面就是SqlSession。
mybatis默认开启了一级缓存,默认的缓存级别是SESSION。
缓存失效的条件:
1、使用useCache,那这个语句不使用缓存
useCache="false"
2、使用flushCache,如果执行这个语句,所有缓存失效
flushCache="false"
3、全局设置localCacheScope,这个原理是每次执行操作后,都会判断是否是SESSION,如果不是清除所有缓存
<setting name="localCacheScope" value="STATEMENT"/>
4、执行增删改操作,以及提交,回滚操作
>这里可以谈一下原因,对于一个事务来说,select查询结果集的变化有增删改后,提交后,以及回滚,这必然导致查询数据的变化,而mybatis也对应这种变化
一级缓存的实现
HashMap实现,所以一级缓存不是线程安全的
获取数据
list = resultHandler == null ? (List<E>) localCache.getObject(key) : null;
直接使用hashmap,key就是cachekey,
@Override
public Object getObject(Object key) {
return cache.get(key);
}
hashmap key的生成
//CachingExecutor中查询的实现
@Override
public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {
BoundSql boundSql = ms.getBoundSql(parameterObject);
//生成key
CacheKey key = createCacheKey(ms, parameterObject, rowBounds, boundSql);
return query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
}
//BaseExecutor
@Override
public CacheKey createCacheKey(MappedStatement ms, Object parameterObject, RowBounds rowBounds, BoundSql boundSql) {
CacheKey cacheKey = new CacheKey();
//id
cacheKey.update(ms.getId());
//这里rowBound可以实现分页,不是sql语句的limit
//可读取的开始位置
cacheKey.update(rowBounds.getOffset());
//可读取的结束位置
cacheKey.update(rowBounds.getLimit());
//语句
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
cacheKey.update(configuration.getEnvironment().getId());
}
return cacheKey;
}
ParameterMapping{property='id', mode=IN, javaType=class java.lang.Integer, jdbcType=null, numericScale=null, resultMapId='null', jdbcTypeName='null', expression='null'}
CacheKey重写了equals,为了hashmap的判断
@Override
public boolean equals(Object object) {
if (this == object) {
return true;
}
if (!(object instanceof CacheKey)) {
return false;
}
final CacheKey cacheKey = (CacheKey) object;
if (hashcode != cacheKey.hashcode) {
return false;
}
if (checksum != cacheKey.checksum) {
return false;
}
if (count != cacheKey.count) {
return false;
}
//比较
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;
}