在 MyBatis 中,引入缓存的目的是为提高查询效率,降低数据库压力。既然 MyBatis
引入了缓存,那么缓存中的key和value的值分别是什么?
可能很容易能回答出value的内容,不就是SQL的查询结果吗。那key是什么呢?是字符串,还是其他什么对象?
如果是字符串的话,那么大家首先能想到的是用SQL语句作为key。但这是不对的,比如:
1
| SELECT * FROM author where id > ?
|
id > 1 和 id > 10 查出来的结果可能是不同的,所以我们不能简单的使用SQL语句作为key
- 运行时参数将会影响查询结果,因此我们的key应该涵盖运行时参数。
- 除此之外,进行分页查询,查询结果也会不同,因此key也应该涵盖分页参数。
综上,我们不能使用简单的 SQL 语句作为 key。应该考虑使用一种复合对象,能涵盖
可影响查询结果的因子。在 MyBatis 中,这种复合对象就是 CacheKey。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| public class CacheKey implements Cloneable, Serializable { private static final int DEFAULT_MULTIPLYER = 37; private static final int DEFAULT_HASHCODE = 17; // 乘子,默认为 37 private final int multiplier; // CacheKey 的 hashCode,综合了各种影响因子 private int hashcode; // 校验和 private long checksum; // 影响因子个数 private int count; // 影响因子集合 private List<Object> updateList; public CacheKey() { this.hashcode = DEFAULT_HASHCODE; this.multiplier = DEFAULT_MULTIPLYER; this.count = 0; this.updateList = new ArrayList<Object>(); } }
|
除了 multiplier 是恒定不变的 ,其他变量将在更新操作中被修改
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| /** 每当执行更新操作时,表示有新的影响因子参与计算 */ public void update(Object object) { int baseHashCode = object == null ? 1 : ArrayUtil.hashCode(object); // 自增 count count++; // 计算校验和 checksum += baseHashCode; // 更新 baseHashCode baseHashCode *= count; // 计算 hashCode hashcode = multiplier * hashcode + baseHashCode; // 保存影响因子 updateList.add(object); }
|
当不断有新的影响因子参与计算时,hashcode 和 checksum 将会变得愈发复杂和随机。
这样可降低冲突率,使 CacheKey 可在缓存中更均匀的分布。CacheKey 最终要作为键存入HashMap,因此它需要覆盖 equals 和 hashCode 方法。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39
| public boolean equals(Object object) { // 检测是否为同一个对象 if (this == object) { return true; } // 检测 object 是否为 CacheKey if (!(object instanceof CacheKey)) { return false; } final CacheKey cacheKey = (CacheKey) object; // 检测 hashCode 是否相等 if (hashcode != cacheKey.hashcode) { return false; } // 检测校验和是否相同 if (checksum != cacheKey.checksum) { return false; } // 检测 coutn 是否相同 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; }
public int hashCode() { // 返回 hashcode 变量 return hashcode; }
|
- equals 方法的检测逻辑比较严格,对CacheKey中多个成员变量进行了检测,已保证两者相等。
- hashCode 方法比较简单,返回 hashcode 变量即可。