什么是缓存?
-
存在内存中的临时数据。
-
将用户经常查询的数据放在缓存(内存)中,用户去查询数据就不用从磁盘上(关系型数据库数据文件)查询,
-
从缓存中查询,从而提高查询效率,解决了高并发系统的性能问题。
为什么使用缓存?
- 减少和数据库的交互次数,减少系统开销,提高系统效率。
什么样的数据能使用缓存?
- 经常查询并且不经常改变的数据。
MyBatis 内置了一个强大的事务性查询缓存机制,它可以非常方便地配置和定制。 为了使它更加强大而且易于配置,我们对 MyBatis 3 中的缓存实现进行了许多改进。
默认情况下,只启用了本地的会话缓存,它仅仅对一个会话中的数据进行缓存。mybatis 也提供了对缓存的支持, 分为一级缓存和二级缓存。 但是在默认的情况下, 只开启一级缓存(一级缓存是对同一个 SqlSession 而言的)。
注:下面的测试案例,idea创建Maven项目,导入相关依赖均不作介绍。
一、一级缓存。
一级缓存是SqlSession级别的缓存(默认是支持一级缓存,不需要在配置文件中配置一级缓存),在操作数据库时,每个SqlSession类的实例对象中有一个数据结构(HashMap)可以用来存储缓存数据,不同的SqlSession类的实例对象缓存的数据区域(HashMap)是互不影响的。当在同一个SqlSession中执行两次相同的sql语句时,第一次执行完毕会将数据写到内存中,第二次查询不执行sql直接从内存中获取。
下面以一个从user表中查找用户为例。
数据库接口:UserMapper.java
//根据id查询用户
User queryUserById(@Param("id") int id);
pojo类:User.java 省略了对应的set、get方法。
public class User {
private int id;
private String username;
private String password;
}
@Test
public void queryUserByIdTest(){
SqlSession sqlSession = MyBatisUtils.getSqlSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
User user1 = mapper.queryUserById(1);
System.out.println(user1);
System.out.println("*******************************************");
User user2 = mapper.queryUserById(1);
System.out.println(user2);
System.out.println(user1==user2);
sqlSession.close();
}
在上面的代码中, 进行了两次查询, 使用相同的 SqlSession。
启动项目:
从上面的日志输出的结果看出,我们可以得出结论:两次的查询情况是不同的:
第一次查询发送了 SQL 语句, 后返回了结果;
第二次查询没有发送 SQL 语句, 直接从内存中获取了结果。
而且两次结果输入一致, 同时断言两个对象相同。
缓存失效的几种情况:
- 查询不同的内容。
@Test
public void queryUserByIdTest(){
SqlSession sqlSession = MyBatisUtils.getSqlSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
User user1 = mapper.queryUserById(1);
System.out.println(user1);
System.out.println("*******************************************");
User user2 = mapper.queryUserById(2);
System.out.println(user2);
System.out.println(user1==user2);
sqlSession.close();
}
- 增删改操作,可能会改变原来的数据,导致刷新缓存。
@Test
public void queryUserByIdTest(){
SqlSession sqlSession = MyBatisUtils.getSqlSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
User user1 = mapper.queryUserById(1);
System.out.println(user1);
mapper.updateUser(new User(2,"zhangsan","1234"));
System.out.println("*******************************************");
User user2 = mapper.queryUserById(1);
System.out.println(user2);
System.out.println(user1==user2);
sqlSession.close();
}
- 查询不同的Mapper.xml
- 手动的清除缓存。
sqlSession.clearCache();
@Test
public void queryUserByIdTest(){
SqlSession sqlSession = MyBatisUtils.getSqlSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
User user1 = mapper.queryUserById(1);
System.out.println(user1);
sqlSession.clearCache(); //手动清理缓存
System.out.println("*******************************************");
User user2 = mapper.queryUserById(1);
System.out.println(user2);
System.out.println(user1==user2);
sqlSession.close();
}
二、二级缓存。
二级缓存也叫全局缓存。
工作机制
- 一个会话查询一条数据,这个数据就会被放在当前会话的一级缓存中;
- 如果当前会话关闭了,这个会话对应的一级缓存就没了;但是我们想要的是,会话关闭了,一级缓存中的数据会被保存到二级缓存中;
- 新的会话查询信息,就可以从二级缓存中获取内容;
- 不同的mapper查出的数据会放在自己对应的缓存(map)中;
首先在Mybatis的核心配置文件中设置:
<!--显式的开启全局缓存-->
<setting name="cacheEnabled" value="true"/>
然后再Mapper中开启二级缓存:
<!--在当前Mapper.xml中使用二级缓存-->
<cache/>
pojo实现序列化接口:
public class User implements Serializable {
private int id;
private String username;
private String password;
}
//二级缓存的使用
@Test
public void queryUserByIdTest2(){
SqlSession sqlSession = MyBatisUtils.getSqlSession();
SqlSession sqlSession2 = MyBatisUtils.getSqlSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
User user=mapper.queryUserById(1);
System.out.println(user);
sqlSession.close();
UserMapper mapper2 = sqlSession2.getMapper(UserMapper.class);
User user2=mapper2.queryUserById(1);
System.out.println(user2);
sqlSession2.close();
}
测试结果:
- 只要开启了二级缓存,在同一个Mapper下就有效
- 所有的数据都会先放在一级缓存中;
- 只有当会话提交,或者关闭的时候,才会提交到二级缓存中!
参考文章:
https://blog.csdn.net/weixin_37139197/article/details/82908377
https://mybatis.org/mybatis-3/zh/sqlmap-xml.html