为了提高数据的使用效率,MyBatis提供了二级缓存机制,将第一次查询出来的数据加载在内存中,当再一次执行相同查询时,MyBatis会先检索内存中是否存在该数据,若存在则直接调用内存中的数据。
一级缓存
一级缓存是默认开启的,存在范围只存在于SqlSession会话中,会随着SqlSession的释放而释放
书写测试方法:
@Test
public void testCacheSelect() {
SqlSession sqlSession = null;
try {
sqlSession = MyBatisUtils.getSqlSession();
Good good = sqlSession.selectOne("good.selectById", 739);
Good good1 = sqlSession.selectOne("good.selectById", 739);
System.out.println(good.hashCode() + ", " + good1.hashCode());
} catch (Exception e) {
e.printStackTrace();
} finally {
MyBatisUtils.close(sqlSession);
}
}
在打印的日志中,可以发现select语句只执行了一次,而两个Good对象也是指向了相同的内存地址。
由此可以看出一级缓存在数据查询中的作用。
如果我们再创建一个SqlSession的连接执行相同的查询语句
@Test
public void testCacheSelect() {
SqlSession sqlSession = null;
try {
sqlSession = MyBatisUtils.getSqlSession();
Good good = sqlSession.selectOne("good.selectById", 739);
System.out.println(good.hashCode());
} catch (Exception e) {
e.printStackTrace();
} finally {
MyBatisUtils.close(sqlSession);
}
try {
sqlSession = MyBatisUtils.getSqlSession();
Good good = sqlSession.selectOne("good.selectById", 739);
System.out.println(good.hashCode());
} catch (Exception e) {
e.printStackTrace();
} finally {
MyBatisUtils.close(sqlSession);
}
}
此时两个Good对象不再指向相同的内存地址且select语句也执行了两遍
二级缓存
二级缓存需要手动开启,生命周期为Mapper Namespace命名空间之内。二级缓存开启后,默认所有的查询操作都会使用缓存,写操作commit提交后为保证数据一致性会强制性清空缓存
在Mapper XML中添加以下配置就可以开启二级缓存
<cache eviction="LRU" flushInterval="60000" size="512" readOnly="true"/>
eviction:缓存的清除策略,当缓存达到上限后会自动触发对应算法清除缓存
清除缓存策略 | 描述 |
---|---|
LRU | 优先移除长时间不使用的对象,当缓存达到上限后新的对象加入时,会用最长时间不被使用的对象进行替换 |
LFU | 优先移除访问次数最少的对象 |
FIFO | 先入先出,按对象进入缓存的顺序进行移除 |
SOFT | 软引用,移除基于垃圾回收状态和软引用规则的对象 |
WEAK | 弱引用,更积极的移除基于垃圾回收状态和弱引用规则的对象 |
flushInterval:清除缓存的时间间隔,在指定时间会自动清理缓存,单位为毫秒
size:缓存存储大小,指定保存对象的个数(List集合也算一个对象),一般不推荐将List集合放入缓存中,因为List集合较为多变,命中率较低
readOnly:是否为只读,设置为true代表数据缓存只读,每次从缓存取出的对象是对象本身,执行效率较高;设置为false代表读取的对象为缓存的副本,每次取出的对象是不同的,这种操作对数据较为安全
在select/insert/update/delete标签中还可以指定是否将查询结果放入缓存或执行后是否直接清除缓存。例如:
<!-- 使用useCashe不将结果集放入缓存中 -->
<select id="selectGoods" resultType="com.mybatis.entity.Good" useCache="false">
SELECT * FROM t_goods ORDER BY goods_id DESC LIMIT 10
</select>
<!-- 针对于某些情景下,想要在插入完成后就清空缓存可以使用flushCache属性,设置为true则会在SQL执行结束后立刻清空缓存 -->
<update id="update" parameterType="com.mybatis.entity.Good" flushCache="true">
UPDATE t_goods SET title = #{title},
sub_title = #{subTitle},
original_cost = #{originalCost},
current_price = #{currentPrice},
discount = #{discount},
is_free_delivery = #{isFreeDelivery},
category_id = #{categoryId}
WHERE goods_id = #{goodsId}
</update>
如上文的测试案例,当添加了二级缓存后再看执行结果