上一篇章我们已经了解了 MyBatis 加载策略,本篇章再来认识一下 MyBatis 缓存。
# 缓存和缓冲的区别
1. 缓冲: buffer
内存中的一块区域,提高单次IO读写效率 (一次读写1024byte,要比一次读写1byte快)
(用空间换时间)
2. 缓存: cache
内存或硬盘中的一块区域, 提高多次读效率(频繁操作)
(用空间换时间)
(
酷狗听歌:
1. 第一次听歌, 这首歌在本地缓存中没有, 要从网络上下载,会下载到硬盘上,作为缓存(比较慢)
2. 再次听歌, 这首歌本地缓存有了, 不需要网络上下载(比较快)
)
“提高再次查询效率的 ”
“经常访问但又不经常修改的数据... ”
缓存是用来提高再次查询效率的,所有的持久层框架基本上都有缓存机制 Mybatis也提供了缓存策略,分为一级缓存,二级缓存
MyBatis 一级缓存是:SqlSession级别的缓存,默认开启,不需要手动配置。
下面我们来写一个案例来验证一下效果。
需求:根据id查询用户
@Test
public void test04(){
//1. 同一个sqlsession (同一用户)
SqlSession session = MyBatisUtil.getSqlSession();
//2. 第一次查询 : 要查询数据库,往缓存中放一份
OrdersMapper mapper = session.getMapper(OrdersMapper.class);
// 查询用户id=41的用户以及拥有的订单
User user = mapper.findUserByIdWithOrders(41);
System.out.println(user);
//3. 第二次查询: 不需要, 因为一级缓存已经有了
OrdersMapper mapper2 = session.getMapper(OrdersMapper.class);
// 查询用户id=41的用户以及拥有的订单
User user2 = mapper.findUserByIdWithOrders(41);
System.out.println(user);
MyBatisUtil.commitAndClose(session);
}
缓存失效:
1. 手动清除缓存 clearCache()
2. 执行SqlSession的C(增加)U(更新)D(删除)操作,会自动清除缓存
3. 事务结束: commit()、rollback(), close()方法, 自动清除缓存
下面我们使用 手动清除缓存 clearCache() 来演示一下缓存失效的情况,如下:
/**
* 一级缓存: 默认开启
*
* 缓存失效:
* 1. 手动清除缓存 clearCache()
* 2. 执行SqlSession的C(增加)U(更新)D(删除)操作,会自动清除缓存
* 3. 事务结束: commit()、rollback(), close()方法, 自动清除缓存
*/
@Test
public void test04(){
//1. 同一个sqlsession (同一用户)
SqlSession session = MyBatisUtil.getSqlSession();
//2. 第一次查询 : 要查询数据库,往缓存中放一份
OrdersMapper mapper = session.getMapper(OrdersMapper.class);
// 查询用户id=41的用户以及拥有的订单
User user = mapper.findUserByIdWithOrders(41);
System.out.println(user);
session.clearCache(); // 手动清除缓存
//3. 第二次查询: 不需要, 因为一级缓存已经有了
OrdersMapper mapper2 = session.getMapper(OrdersMapper.class);
// 查询用户id=41的用户以及拥有的订单
User user2 = mapper.findUserByIdWithOrders(41);
System.out.println(user);
MyBatisUtil.commitAndClose(session);
}
一级缓存是SqlSession范围的缓存,不同的SqlSession之间的缓存区域是互相不影响的,执行SqlSession的C(增加)U(更新)D(删除)操作,或者调用clearCache()、commit()、close()方法,都会清空缓存。
MyBatis的二级缓存虽然是默认开启的,但需要在映射文件中配置<cache/>
标签才能使用,而且要求实体类的必须实现序列化接口
根据上图,前面一级缓存是指默认一次会话中的查询缓存,而二级缓存则是针对一个映射查询的多次会话的查询缓存。
下面我们来配置一下,配置一个订单表查询的二级缓存。
<settings>
<!--
因为cacheEnabled的取值默认就为true,所以这一步可以省略不配置。
为true代表开启二级缓存;为false代表不开启二级缓存。
-->
<setting name="cacheEnabled" value="true"/>
</settings>
在 sqlMapConfig.xml
设置如下:
设置订单映射配置文件 OrdersMapper.xml
的二级缓存,如下:
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!--
实体类的映射文件
namespace 指定接口的类全名
-->
<mapper namespace="com.lijw.dao.OrdersMapper">
<!-- 设置二级缓存 -->
<cache></cache>
</mapper>
/**
* 二级缓存:
* 1. 作用范围: 比一级大, 跨sqlsession
* 2. 存储介质: 内存/硬盘
* <p>
* 注意:
* 1. 核心配置文件: 默认开启,可以不设置
* 2. 映射文件: <cache/>
* 3. 实体类: 实现serializable接口
* <p>
* 开发中经常不用二级缓存
* 1. 二级缓存数据主要在硬盘上 / 数据库的数据也在硬盘上
* 已经查好的数据 还得查
* <p>
* 效率提升不明显
* 2. 以后用其他的技术代替mybatis 二级缓存(redis非关系型数据库)
*/
@Test
public void test05() {
//1. 模拟第一个用户查询
SqlSession session = MyBatisUtil.getSqlSession();
OrdersMapper mapper = session.getMapper(OrdersMapper.class);
// 查询用户id=41的用户以及拥有的订单
User user = mapper.findUserByIdWithOrders(41);
System.out.println(user);
// 关闭第一个用户的查询 session
MyBatisUtil.commitAndClose(session);
//3. 模拟第二个用户查询(不同的SqlSession)
SqlSession session2 = MyBatisUtil.getSqlSession();
OrdersMapper mapper2 = session2.getMapper(OrdersMapper.class);
// 查询用户id=41的用户以及拥有的订单
User user2 = mapper2.findUserByIdWithOrders(41);
System.out.println(user2);
// 关闭第二个用户的查询 session
MyBatisUtil.commitAndClose(session2);
}
执行查看是否执行了两次SQL,如下:
二级缓存是mapper映射级别的缓存,多个SqlSession去操作同一个Mapper映射的sql语句,多个SqlSession可以共用二级缓存,二级缓存是跨SqlSession的。
二级缓存相比一级缓存的范围更大.
1. mybatis的缓存,都不需要我们手动存储和获取数据。mybatis自动维护的。
2. 使用mybatis,如果是中小型项目,使用自带缓存的机制是可以满足需求的。如果是大型(分布式)项目,mybatis的缓存灵活性不足,需要使用第三方的缓存技术解决问题。