实体类
Student
@Data
@Table(name = "student")
public class StudentEntity implements Serializable {
private static final long serialVersionUID = 1L;
@Id
@Column(name = "id")
private Long id;
@Column(name = "name")
private String name;
@Column(name = "class_id")
private Long classId;
@Transient
private String className;
}
Class
@Data
@Table(name = "class")
public class ClassEntity implements Serializable {
private static final long serialVersionUID = 1L;
@Id
@Column(name = "id")
private Long id;
@Column(name = "name")
private String name;
}
表结构
CREATE TABLE `student` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(255) DEFAULT NULL,
`class_id` int(11) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8;
CREATE TABLE `class` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(255) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8;
一级缓存
一级缓存是sqlSession
级别的,二级缓存是SqlSessionFactory
级别的
一级缓存-测试1
@Autowired
private SqlSessionFactory sqlSessionFactory;
@Test
public void t1() {
SqlSession sqlSession = sqlSessionFactory.openSession();
// 同一个sqlSession,同一个mapper实例,执行相同的查询语句
StudentMapper studentMapper = sqlSession.getMapper(StudentMapper.class);
studentMapper.selectAll();
studentMapper.selectAll();
// 结论:只会出现1次sql语句,第二次查询走缓存
}
一级缓存-测试2
@Test
public void t2() {
SqlSession sqlSession = sqlSessionFactory.openSession();
// 同一个sqlSession,不同的mapper实例,执行相同的查询语句
StudentMapper studentMapper = sqlSession.getMapper(StudentMapper.class);
studentMapper.selectAll();
StudentMapper studentMapper2 = sqlSession.getMapper(StudentMapper.class);
studentMapper2.selectAll();
// 结论:只会出现1次sql语句,第二次查询走缓存
}
一级缓存-测试3
@Test
public void t3() {
// 不同sqlSession,执行相同的查询语句
SqlSession sqlSession = sqlSessionFactory.openSession();
SqlSession sqlSession2 = sqlSessionFactory.openSession();
StudentMapper studentMapper = sqlSession.getMapper(StudentMapper.class);
studentMapper.selectAll();
StudentMapper studentMapper2 = sqlSession2.getMapper(StudentMapper.class);
studentMapper2.selectAll();
// 结论:出现2次sql语句,没有缓存效果
}
一级缓存-测试4(update)
@Test
public void t4() {
SqlSession sqlSession = sqlSessionFactory.openSession();
// 第一次查询
StudentMapper studentMapper = sqlSession.getMapper(StudentMapper.class);
List<StudentEntity> list = studentMapper.selectAll();
// 更新操作
StudentEntity student = list.get(0);
student.setName("修改学生姓名");
studentMapper.updateByPrimaryKey(student);
// 第二次查询
studentMapper.selectAll();
// 结论:第一次会出现sql语句,由于进行了更新操作,导致缓存失效,第二次查询也会重新查询sql
}
一级缓存-测试5(update)
和测试4类似,但是update
的对象改为class
,看看是否会影响student
的查询
@Test
public void t5() {
SqlSession sqlSession = sqlSessionFactory.openSession();
// 第一次查询student
StudentMapper studentMapper = sqlSession.getMapper(StudentMapper.class);
List<StudentEntity> list = studentMapper.selectAll();
// 更新class
ClassMapper classMapper = sqlSession.getMapper(ClassMapper.class);
ClassEntity classx = classMapper.selectAll().get(0);
classx.setName("修改班级姓名");
classMapper.updateByPrimaryKey(classx);
// 第二次查询student
studentMapper.selectAll();
// 结论:第一次会出现sql语句,由于进行了更新操作(即使不是同一个mapper实例),也会导致缓存失效,第二次查询也会重新查询sql
}
一级缓存-测试6(springboot失效)
@Autowired
private StudentMapper studentMapper;
@Test
public void t6() {
// 使用mybatis整合springboot后,直接查询2次
studentMapper.selectAll();
studentMapper.selectAll();
// 结论:会出现2次sql查询,缓存失效,因为每一次sql语句的执行都是使用新的sqlSession
}
二级缓存
一级缓存默认开启,二级缓存默认关闭
开启配置
mybatis:
configuration:
cache-enabled: true
给mapper添加<cache/>
,前面用的通用mapper的selectAll
方法,二级缓存一直没生效,这里重新手写一个查询方法getAll,再加上useCache
配置才行
<?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.tw.nr.network.dao.mapper.StudentMapper">
<cache/><!--开启二级缓存-->
<!--获取学生列表-->
<select id="getAll" resultType="com.tw.nr.network.entity.StudentEntity" useCache="true">
select * from student;
</select>
<!--获取学生详细信息-->
<select id="getStudentDetail" resultType="com.tw.nr.network.entity.StudentEntity" useCache="true">
SELECT
a.`name`,
b.`name` AS className
FROM
student a
JOIN class b ON a.class_id = b.id
where
a.id=#{id}
</select>
</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.tw.nr.network.dao.mapper.ClassMapper">
<cache/><!--开启二级缓存-->
</mapper>
二级缓存-测试1
@Test
public void t7() {
// 不同sqlSession,执行相同的查询语句
SqlSession sqlSession = sqlSessionFactory.openSession();
SqlSession sqlSession2 = sqlSessionFactory.openSession();
StudentMapper studentMapper = sqlSession.getMapper(StudentMapper.class);
studentMapper.getAll();
sqlSession.commit();
StudentMapper studentMapper2 = sqlSession2.getMapper(StudentMapper.class);
studentMapper2.getAll();
// 结论:只会查询1次sql语句,第二次查询走缓存
}
二级缓存-测试2(脏数据)
二级缓存不适合多表查询,因为student
和class
处于不同的命名空间,在student
的命名空间执行了sqlA
的联表查询(student和class联表),进行了缓存,然后class
执行了更新,但这并不会刷新sqlA
的缓存,导致sqlA
还是读取到了脏数据
@Test
public void t8() {
SqlSession sqlSession = sqlSessionFactory.openSession();
// 第一次查询学生的详细信息(联查了class表)
StudentMapper studentMapper = sqlSession.getMapper(StudentMapper.class);
StudentEntity student = studentMapper.getStudentDetail(1L);
System.out.println(student);
sqlSession.commit();
// 更新了class表
ClassMapper classMapper = sqlSession.getMapper(ClassMapper.class);
ClassEntity classx = classMapper.selectAll().get(0);
classx.setName("班级1000");
classMapper.updateByPrimaryKey(classx);
sqlSession.commit();
// 再次查询学生的详细信息
student = studentMapper.getStudentDetail(1L);
System.out.println(student);
/**
* 结论:读取到了脏数据,二级缓存不适合多表查询,因为student和class处于不同的命名空间,在student的命名空间执行了sqlA的联表查询(student和class联表),进行了缓存
* 然后class执行了更新,但这并不会刷新sqlA的缓存,导致sqlA还是读取到了脏数据
* */
}