文章目录
- 一、一级缓存
- 1.1 简介
- 1.2 一级缓存的失效情况
- 二、二级缓存
- 2.1 简介
- 2.2 二级缓存的使用
学习地址🔗
- https://www.bilibili.com/video/BV1mW411M737
- https://www.bilibili.com/video/BV1NE411Q7Nx
- 官网文档
一、一级缓存
1.1 简介
💬概述:一级缓存也称为本地缓存、SqlSession级别的缓存
🔑特点
- 一级缓存是一直开启的,不能手动关闭
- 一级缓存的作用域是MyBatis与数据库之间的一次会话,在本次会话间第一次查询出来的数据都会放到一级缓存中(本地缓存)
- 一个
SqlSession
会话对象对应一个一级缓存,因为SqlSession
对象就代表MyBatis与数据库的一次会话 - 一级缓存相当于SqlSession级别的一个
Map
集合,在本次会话中查询相同数据时,就不会再访问数据库,而是直接从该Map
集合(一级缓存)中获取,可以大大加快访问速度
🔑测试:在与数据库的一次会话间查询两次相同的对象数据,然后比较两个对象是否是同一个对象
-
测试方法
@Test public void testFirstLevelCache() { // 获取SqlSession对象(代表一次会话) SqlSession sqlSession = MyBatisUtil.getSqlSession(); // 获取mapper对象 UserDao mapper = sqlSession.getMapper(UserDao.class); // 第一次查询员工user1 User user1 = mapper.getUserById(2); System.out.println(user1); // 再次查询员工user2 User user2 = mapper.getUserById(2); System.out.println(user2); // 比较两个对象是否相同 System.out.println("查询的两个对象是否相同 --> " + (user1 == user2)); // 关闭sqlSession sqlSession.close(); }
-
打印结果
-
结果分析
- 控制台的打印结果中只有一条SQL语句,说明MyBatis只发送了一条SQL语句给数据库,也就是说MyBatis与数据库只进行了一次交互
- 打印结果中可以看到两个对象的比较结果是true,说明两次查询出的对象是相同的
- 第一次查询对象
user1
时,此时本地缓存(一级缓存)中还没有对应的对象,因此MyBatis就会访问数据库获取对应数据,然后再把获取到的对象数据存储到本地缓存中;当第二次再查询相同id的对象user2
时,MyBatis就会直接到缓存中查找对应数据,而不会再去访问数据库,因此也只会发送一次SQL语句
1.2 一级缓存的失效情况
💬概述:是一级缓存是SqlSession级别的缓存,只在一次会话间生效,当SqlSession
对象发生改变(当前会话不一定结束)时一级缓存就有可能会失效
🔑四种失效情况
-
两次查询一样的数据,但使用的
SqlSession
对象不同-
测试方法
@Test public void testFirstLevelCacheLose() { // 获取SqlSession01 SqlSession sqlSession01 = MyBatisUtil.getSqlSession(); // 直接查询对象user01 User user01 = sqlSession01.getMapper(UserDao.class).getUserById(1); System.out.println(user01); // 获取SqlSession02 SqlSession sqlSession02 = MyBatisUtil.getSqlSession(); // 直接查询对象user02 User user02 = sqlSession02.getMapper(UserDao.class).getUserById(1); System.out.println(user02); // 比较两个对象是否相同 System.out.println("查询的两个对象是否相同 --> " + (user01 == user02)); // 关闭两个sqlSession sqlSession01.close(); sqlSession02.close(); }
-
打印结果
-
-
同一个
SqlSession
对象下,两次查询不一样的数据-
测试方法
@Test public void testFirstLevelCacheLose() { // 获取SqlSession01 SqlSession sqlSession01 = MyBatisUtil.getSqlSession(); UserDao mapper = sqlSession01.getMapper(UserDao.class); // 直接查询对象user01 User user01 = mapper.getUserById(1); System.out.println(user01); // 直接查询对象user02 User user02 = mapper.getUserById(2); System.out.println(user02); // 比较两个对象是否相同 System.out.println("查询的两个对象是否相同 --> " + (user01 == user02)); // 关闭sqlSession sqlSession01.close(); }
-
打印结果
-
-
同一个
SqlSession
对象下,两次查询一样的数据,但在第二次查询前执行一次增删改操作-
测试方法
@Test public void testFirstLevelCacheLose() { // 获取SqlSession01 SqlSession sqlSession01 = MyBatisUtil.getSqlSession(); UserDao mapper = sqlSession01.getMapper(UserDao.class); // 直接查询对象user01 User user01 = mapper.getUserById(1); System.out.println(user01); // 添加一条数据 int result = mapper.insertUserBySqlAndInclude(new User(null, "刘唐", "liu123")); if (result != 0) { System.out.println("插入成功!"); } // 直接查询对象user02 User user02 = mapper.getUserById(1); System.out.println(user02); // 比较两个对象是否相同 System.out.println("查询的两个对象是否相同 --> " + (user01 == user02)); // 关闭sqlSession sqlSession01.close(); }
-
打印结果
-
-
同一个
SqlSession
对象下,两次查询一样的数据,但在第二次查询前将缓存清空(调用SqlSession
对象的clearCache()
方法)-
测试方法
@Test public void testFirstLevelCacheLose() { // 获取SqlSession01 SqlSession sqlSession01 = MyBatisUtil.getSqlSession(); UserDao mapper = sqlSession01.getMapper(UserDao.class); // 直接查询对象user01 User user01 = mapper.getUserById(1); System.out.println(user01); // 清空本地缓存 sqlSession01.clearCache(); System.out.println("本地缓存已清空..."); // 直接查询对象user02 User user02 = mapper.getUserById(1); System.out.println(user02); // 比较两个对象是否相同 System.out.println("查询的两个对象是否相同 --> " + (user01 == user02)); // 关闭sqlSession sqlSession01.close(); }
-
打印结果
-
二、二级缓存
2.1 简介
💬概述:MyBatis还提供了二级缓存机制,二级缓存也称为全局缓存,范围比一级缓存更大
🔑特点
- 二级缓存是基于namespace级别的缓存,一个
namespace
对应一个二级缓存,即一个SQL映射文件对应一个二级缓存,也相当于一个dao接口对应一个二级缓存 - 二级缓存在MyBatis底层也是一个
Map
集合,作用域是整个mapper,是namespace级别的Map
集合
🔑工作机制
-
在一次会话中,即一个
SqlSession
对象中查询的数据会先放入当前会话对应的本地缓存(一级缓存)中 -
当前会话关闭后,MyBatis不会将会话对应的一级缓存中数据清空,而是会将数据转移到对应mapper的二级缓存中
❓ 关于缓存数据的转移
- 一定是会话关闭后,即执行
sqlSession.close()
后,一级缓存中的数据才会被转移到二级缓存,如果当前会话还没有关闭,数据还是只在一级缓存中 - MyBatis是通过序列化和反序列的方式将数据从一级缓存克隆到二级缓存中,因此二级缓存和一级缓存中的数据内容虽然是一样的,但却不是同一份数据
- 一定是会话关闭后,即执行
-
当新的会话创建时,即创建新的
SqlSession
对象查询数据时,如果开启了全局二级缓存,则MyBatis会先从二级缓存中查询对应数据,如果二级缓存中查询不到,才会到一级缓存中查询,如果一级缓存中也没有,最后才访问数据库查找
2.2 二级缓存的使用
-
开启全局二级缓存配置:在全局配置文件的
<setting>
标签中设置cacheEnabled
参数,并设置为true<!-- 设置MyBatis运行时的参数 --> <settings> <!-- 开启全局二级缓存配置 --> <setting name="cacheEnabled" value="true"/> </settings>
-
在映射文件中添加
<cache>
标签:直接在对应的映射文件中添加<cache>
标签(与其他SQL语句标签同级),<cache>
里面的属性可以不设置,MyBatis已经设置好了默认值<mapper namespace="com.key.mybatis.dao.UserDao"> <!-- 在当前映射文件中开启二级缓存 --> <cache/> </mapper>
-
在映射文件对应的JavaBean中实现序列化接口:在对应的JavaBean类中实现序列化接口——
Serializable
public class User implements Serializable { /** * 序列化id */ private static final long serialVersionUID = -6976094896694250242L; private Integer userid; private String username; private String password; public User() { } // code... }
-
测试方法
@Test public void testSecondLevelCache() { // 获取两个sqlSession SqlSession sqlSession01 = MyBatisUtil.getSqlSession(); SqlSession sqlSession02 = MyBatisUtil.getSqlSession(); // 使用sqlSession01查询数据 User user01 = sqlSession01.getMapper(UserDao.class).getUserById(4); System.out.println(user01); // 关闭sqlSession01 sqlSession01.close(); // 使用sqlSession02查询相同的数据 User user02 = sqlSession02.getMapper(UserDao.class).getUserById(4); System.out.println(user02); // 再关闭sqlSession02 sqlSession02.close(); // 比较获取的两个对象是否是同一个 System.out.println("两个数据是否相同 --> " + (user01 = user02)); }
-
打印结果