mybatis有一级缓存和二级缓存,默认开启一级缓存;在查询的时候会先查缓存,缓存中没有数据才会查询数据库;那么是根据什么来确定当前要查询的数据已经在缓存中呢?一个叫cacheKey对象,可以确定前要查询的数据是否在缓存中。
cacheKey和我们缓存的数据有什么联系呢?由于一条查询语句,会查询到相应的数据;所以会根据查询语句的sql还有其他的数据生成cacheKey作为键值,查询到的数据作为value保存到缓存中;这样就可以通过cachekey来获取缓存中的数据了。
源码分析
我们先看CacheKey的结构:
private static final int DEFAULT_MULTIPLIER = 37;
private static final int DEFAULT_HASHCODE = 17;
/**计算hashcode的乘积数默认:37*/
private final int multiplier;
/**hashcode默认17*/
private int hashcode;
private long checksum;
private int count;
private List<Object> updateList;
/**新建对象,初始化数据*/
public CacheKey() {
this.hashcode = DEFAULT_HASHCODE;
this.multiplier = DEFAULT_MULTIPLIER;
this.count = 0;
this.updateList = new ArrayList<>();
}
/**
*1.将参数object添加到uplist对象中
*2.计算hashcode,checksum,count
*
*/
public void update(Object object) {
int baseHashCode = object == null ? 1 : ArrayUtil.hashCode(object);
count++;
checksum += baseHashCode;
baseHashCode *= count;
hashcode = multiplier * hashcode + baseHashCode;
updateList.add(object);
}
/**
*比较cacheKey是否相等;
*主要是比较cacheKey的几个属性是否相等;
*
*
*
*/
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;
}
cacheKey比较重要的2个方法:
- update:生成cacheKey,主要是往uplist中填充值;
- equals:比较2个key是否相等;判断cacheKey是否相等:主要是比较cacheKey的属性:hashcode ,checksum ,count ,updateList;
cacheKey如何生成(主要看uplist中填充了哪些值)?是BaseExcutor类中的方法createCacheKey:
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<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;
}
这个方法中可以看到一共调用update方法6次;
uplist: id+offset+limit+sql+parameterValue+environmentId
实际cachekey:
再分别解释一下uplist中数据的含义
id:全限定类名+方法名
offset:偏移量(默认:0)
limit:查询数据条数(默认:Integer.MAX_VALUE)
sql:查询语句
parameterValue:查询参数的值
environmentId:在配置mybatis环境的时候有一个标签:environment ,他有一个属性id;environmentId的就是这个id的值;