七、Mybatis 查询缓存
缓存:将数据临时存储在存储介质(内存,文件)中,关系型数据库的缓存目的就是为了减轻数据库的压力。
数据库的数据实际是存储在硬盘中,如果我们程序需要用到数据,那么就需要频繁的从磁盘中读取数据,效率低,数据库压力大。我们可以把查询到的数据缓存起来,这样就减少了频繁操作磁盘数据,提高查询效率,减轻服务器压力。
Mybatis提供了查询缓存,用于减轻数据库压力,提高数据库性能。但是在实际项目开发中,很少使用Mybatis的缓存机制,现在主流的缓存机制是redis。
7.1 什么是查询缓存
-
MyBatis提供查询缓存,用于减轻数据库压力,提高数据库性能;
-
MyBatis提供一级缓存,和二级缓存;
-
一级缓存是SqlSession级别的缓存。在操作数据库时需要构造SqlSession对象,在对象中有一个数据结构(HashMap)用于存储缓存数据。不同的SqlSession之间的缓存数据区域(HashMap)是互相不影响的;
-
二级缓存是mapper级别的缓存,多个SqlSession去操作同一个mapper的sql语句,多个SqlSession可以共用二级缓存,二级缓存是跨SqlSession的;
7.2 一级缓存
7.2.1 一级缓存工作原理
-
第一次查询id为1的数据,先去找一级缓存中查找是否有id为1的数据,如果没有,从数据库中查询该数据,并将该数据存储到一级缓存中;
-
第二次查询id为1的数据,也先去找一级缓存中查找是否有id为1的数据,缓存中有,直接从缓存中获取该数据,不再查询数据库;
-
如果SqlSession去执行commit操作(执行插入、更新、删除),将清空SqlSession中的一级缓存,这样做的目的为了让缓存中存储的是最新的数据,避免脏读;
7.2.2 实体类
public class Person {
private Integer id;
private String personName;
private Integer personAge;
private String personAddress;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getPersonName() {
return personName;
}
public void setPersonName(String personName) {
this.personName = personName;
}
public Integer getPersonAge() {
return personAge;
}
public void setPersonAge(Integer personAge) {
this.personAge = personAge;
}
public String getPersonAddress() {
return personAddress;
}
public void setPersonAddress(String personAddress) {
this.personAddress = personAddress;
}
@Override
public String toString() {
return "Person{" +
"id=" + id +
", personName='" + personName + '\'' +
", personAge=" + personAge +
", personAddress='" + personAddress + '\'' +
'}';
}
}
7.2.3 mapper接口
public interface PersonMapper {
Person selectById(Integer id);
List<Person> select();
}
7.2.4 mapper文件
<?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">
<mapper namespace="com.newcapec.mapper.PersonMapper">
<select id="selectById" parameterType="java.lang.Integer" resultType="com.newcapec.entity.Person">
select id,person_name,person_age,person_address from person where id=#{id}
</select>
<select id="select" resultType="com.newcapec.entity.Person">
select id,person_name,person_age,person_address from person
</select>
</mapper>
7.2.5 测试
public class CacheTest {
@Test
public void testSqlSessionCache() {
SqlSession sqlSession = MybatisUtil.getSession();
PersonMapper personMapper = sqlSession.getMapper(PersonMapper.class);
System.out.println("----------第一次使用id为1的person数据-----------");
Person p1 = personMapper.selectById(1);
System.out.println(p1);
/**
* 一级缓存自带缓存,不可不用的,缓存介质为内存
* commit()提交方法可以请求缓存
*/
//sqlSession.commit();
System.out.println("----------第二次使用id为1的person数据-----------");
Person p2 = personMapper.selectById(1);
System.out.println(p2);
sqlSession.close();
}
}
7.3 二级缓存
7.3.1 二级缓存工作原理
-
SqlSession1去查询id为1的数据,查询到后会将该数据存储到二级缓存中;
-
SqlSession2去查询id为1的数据,去缓存中找是否存在数据,如果存在直接从缓存中取出数据;
-
如果SqlSession3去执行相同mapper下sql,执行commit提交,清空该mapper下的二级缓存区域的数据;
-
二级缓存与一级缓存区别,二级缓存的范围更大,多个SqlSession可以共享一个Mapper的二级缓存区域;
-
每个mapper有一个二级缓存区域,按namespace分;
-
如果两个mapper的namespace如果相同,这两个mapper执行sql查询到数据将存在相同的二级缓存区域中;
7.3.2 开启二级缓存
在mybatis核心配置文件中配置:cacheEnabled
设置项 | 描述 | 允许值 | 默认值 |
---|---|---|---|
cacheEnabled | 对在此配置文件下的所有cache 进行全局性开/关设置 | true \ false | true |
<!-- 全局参数设置 -->
<settings>
<!-- 开启二级缓存-->
<setting name="cacheEnabled" value="true"/>
</settings>
在映射文件中开启二缓存,mapper.xml下的SQL执行完成会存储到它的缓存区域HashMap。
<mapper namespace="com.newcapec.mapper.PersonMapper">
<!-- 配置当前mapper文件中所有查询语句都放入二级缓存中 -->
<cache/>
<select>
...
</select>
</mapper>
7.3.3 实体类
二级缓存中存储数据的实体类必须实现可序列化接口java.io.Serializable。
public class Person implements Serializable {
}
7.3.4 二级缓存测试
@Test
public void testMapperCache(){
/**
* 二级缓存,可插拔式缓存,缓存介质:内存+磁盘
*/
SqlSession sqlSession1 = MybatisUtil.getSession();
PersonMapper personMapper1 = sqlSession1.getMapper(PersonMapper.class);
System.out.println("-------------第一次查询--------------");
Person p1 = personMapper1.selectById(2);
System.out.println(p1);
sqlSession1.close();
System.out.println("----------------sqlSession1关闭,建立sqlSession2连接-------------------");
SqlSession sqlSession2 = MybatisUtil.getSession();
PersonMapper personMapper2 = sqlSession2.getMapper(PersonMapper.class);
System.out.println("-------------第二次查询--------------");
Person p2 = personMapper2.selectById(2);
System.out.println(p2);
sqlSession2.close();
}
7.3.5 useCache配置
在statement中设置useCache="false"可以禁用当前select语句的二级缓存,即每次查询都会发出sql去查询。默认情况是true,即该sql使用二级缓存。
<?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">
<mapper namespace="com.newcapec.mapper.PersonMapper">
<!-- 配置当前mapper文件中所有查询语句都放入二级缓存中 -->
<cache/>
<select id="selectById" parameterType="java.lang.Integer" resultType="com.newcapec.entity.Person" useCache="true">
select id,person_name,person_age,person_address from person where id=#{id}
</select>
<!-- select标签中的useCache属性: 决定了当前sql是否使用二级缓存,默认为true -->
<select id="select" resultType="com.newcapec.entity.Person" useCache="false">
select id,person_name,person_age,person_address from person
</select>
</mapper>
测试:
@Test
public void testMapperCache2() {
SqlSession sqlSession1 = MybatisUtil.getSession();
PersonMapper personMapper1 = sqlSession1.getMapper(PersonMapper.class);
System.out.println("-------------第一次查询--------------");
List<Person> list1 = personMapper1.select();
System.out.println(list1);
sqlSession1.close();
System.out.println("----------------sqlSession1关闭,建立sqlSession2连接-------------------");
SqlSession sqlSession2 = MybatisUtil.getSession();
PersonMapper personMapper2 = sqlSession2.getMapper(PersonMapper.class);
System.out.println("-------------第二次查询--------------");
List<Person> list2 = personMapper2.select();
System.out.println(list2);
sqlSession2.close();
}