当我们大量执行重复的查询SQL语句的时候,会频繁的和数据库进行通信,会增加查询时间等影响用户体验的问题,可以通过缓存,以降低网络流量,使网站加载速度更快.
默认情况下,MyBatis只启用了本地的会话缓存,它仅仅对一个会话中的数据进行缓存。这也就是大家常说的MyBatis一级缓存,一级缓存的作用域是SqlSession。
代码验证
//测试一级缓存
@Test
public void testCache() throws IOException {
//加载配置文件
InputStream rs = Resources.getResourceAsStream("MyBatisConfig.xml");
//获取工厂建造类
SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
//获取工厂对象
SqlSessionFactory build = sqlSessionFactoryBuilder.build(rs);
//获取SqlSession对象
SqlSession sqlSession = build.openSession(true);
//得到代理的对象
StudentMapper mapper = sqlSession.getMapper(StudentMapper.class);
//根据名字查询
Student stu1 = mapper.findStudentByName("小花花");
Student stu2 = mapper.findStudentByName("小花花");
/*
这里相等,说明默认开启一级缓存,就是在同一个sqlSession域中有效,只要查询相同的语句,就是从一级缓存中获取
*/
System.out.println("两次查询结果的学生对象是否是同一个:"+(stu1==stu2));
//关闭会话
sqlSession.close();
rs.close();
}
执行结果
由上面的执行结果得知
MyBatis是默认开启一级缓存的,作用域是同一个SqlSession,在同一个sqlSession中查询相同的sql语句,第一次查询后会将结果对象存放在一级缓存中,在后面查询相同的sql语句的时候会在一级缓存中去获取,不用在与数据库进行通信,大大降低了查询速度.
** 代码演示**
//测试一级缓存
@Test
public void testCache() throws IOException {
//加载配置文件
InputStream rs = Resources.getResourceAsStream("MyBatisConfig.xml");
//获取工厂建造类
SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
//获取工厂对象
SqlSessionFactory build = sqlSessionFactoryBuilder.build(rs);
//获取SqlSession对象
SqlSession sqlSession = build.openSession(true);
//得到代理的对象
StudentMapper mapper = sqlSession.getMapper(StudentMapper.class);
//根据名字查询
Student stu1 = mapper.findStudentByName("小花花");
Student stu2 = mapper.findStudentByName("小付");
System.out.println("两次查询结果的学生对象是否是同一个:"+(stu1==stu2));
//关闭会话
sqlSession.close();
rs.close();
}
** 代码演示**
//测试一级缓存
@Test
public void testCache() throws IOException {
//加载配置文件
InputStream rs = Resources.getResourceAsStream("MyBatisConfig.xml");
//获取工厂建造类
SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
//获取工厂对象
SqlSessionFactory build = sqlSessionFactoryBuilder.build(rs);
//获取SqlSession对象
SqlSession sqlSession = build.openSession(true);
StudentMapper mapper = sqlSession.getMapper(StudentMapper.class);
//获取SqlSession2对象
SqlSession sqlSession2 = build.openSession(true);
StudentMapper mapper2 = sqlSession2.getMapper(StudentMapper.class);
//根据名字查询
Student stu1 = mapper.findStudentByName("小花花");
Student stu2 = mapper2.findStudentByName("小花花");
System.out.println("两次查询结果的学生对象是否是同一个:"+(stu1==stu2));
//关闭会话
sqlSession.close();
rs.close();
}
代码演示
//测试一级缓存
@Test
public void testCache() throws IOException {
//加载配置文件
InputStream rs = Resources.getResourceAsStream("MyBatisConfig.xml");
//获取工厂建造类
SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
//获取工厂对象
SqlSessionFactory build = sqlSessionFactoryBuilder.build(rs);
//获取SqlSession对象
SqlSession sqlSession = build.openSession(true);
//得到代理的对象
StudentMapper mapper = sqlSession.getMapper(StudentMapper.class);
//根据名字查询
Student stu1 = mapper.findStudentByName("小花花");
//修改数据
stu1.setAge(18);
//执行更新语句
mapper.updateStudent(stu1);
//再次查询
Student stu2 = mapper.findStudentByName("小花花");
System.out.println("两次查询结果的学生对象是否是同一个:"+(stu1==stu2));
//关闭会话
sqlSession.close();
rs.close();
}
MyBatis一级缓存的运行过程是这样的:执行SQL语句的过程中,首次执行它时从数据库获取的所有数据会被存储在一段高速缓存中,今后执行这条语句时就会从高速缓存中读取结果,而不是再次查询数据库。MyBatis提供了默认下基于Java HashMap的缓存实现。
重点是要明白:MyBatis执行SQL语句之后,这条语句的执行结果被缓存,以后再执行这条语句的时候,会直接从缓存中拿结果,而不是再次执行SQL。但是一旦执行新增或更新或删除操作,缓存就会被清除。下面将分情况来验证一下。
很明显,以上各种情况验证了一级缓存的概念,在同个SqlSession中,查询语句相同的sql会被缓存,但是一旦执行新增或更新或删除操作,缓存就会被清除。
MyBatis 一级缓存最大的共享范围就是一个SqlSession内部,那么如果多个 SqlSession 需要共享缓存,则需要开启二级缓存.
当二级缓存开启后,同一个命名空间(namespace) 所有的操作语句,都影响着一个共同的 cache,也就是二级缓存被多个 SqlSession 共享,是一个全局的变量。当开启缓存后,数据的查询执行的流程就是 二级缓存 -> 一级缓存 -> 数据库。
二级缓存开启条件
二级缓存默认是不开启的,需要手动开启二级缓存,实现二级缓存的时候,MyBatis要求返回的**POJO必须是可序列化的**。开启二级缓存的条件也是比较简单.
<!-- 通知 MyBatis 框架开启二级缓存 -->
<settings>
<setting name="cacheEnabled" value="true"/>
</settings>
<!-- 表示DEPT表查询结果保存到二级缓存(共享缓存)
cache 标签有多个属性,一起来看一些这些属性分别代表什么意义
eviction: 缓存回收策略,有这几种回收策略
LRU - 最近最少回收,移除最长时间不被使用的对象
FIFO - 先进先出,按照缓存进入的顺序来移除它们
SOFT - 软引用,移除基于垃圾回收器状态和软引用规则的对象
WEAK - 弱引用,更积极的移除基于垃圾收集器和弱引用规则的对象
默认是 LRU 最近最少回收策略
flushinterval 缓存刷新间隔,缓存多长时间刷新一次,默认不清空,设置一个毫秒值
readOnly: 是否只读;true 只读,MyBatis 认为所有从缓存中获取数据的操作都是只读操作,不会修改数据。MyBatis 为了加快获取数据,直接就会将数据在缓存中的引用交给用户。不安全,速度快。读写(默认):MyBatis 觉得数据可能会被修改
size : 缓存存放多少个元素
type: 指定自定义缓存的全类名(实现Cache 接口即可)
blocking: 若缓存中找不到对应的key,是否会一直blocking,直到有对应的数据进入缓存。
我这里就使用默认的,不去设置参数
-->
<cache
eviction="FIFO"
flushInterval="60000"
size="512"
readOnly="true"
/>
@Data //这个注解是lombok包下的,可以自动帮我们添加getset方法等
public class Student implements Serializable {
private Integer sid;
private String name;
private Integer age;
}
//测试一级缓存
@Test
public void testCache() throws IOException {
//加载配置文件
InputStream rs = Resources.getResourceAsStream("MyBatisConfig.xml");
//获取工厂建造类
SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
//获取工厂对象
SqlSessionFactory build = sqlSessionFactoryBuilder.build(rs);
//获取SqlSession对象
SqlSession sqlSession = build.openSession(true);
StudentMapper mapper = sqlSession.getMapper(StudentMapper.class);
//根据名字查询
Student stu1 = mapper.findStudentByName("小花花");
System.out.println("第一次查出的对象"+stu1);
//关闭会话后,会将一级缓存的数据保存在二级缓存中
sqlSession.close();
//获取SqlSession2对象
SqlSession sqlSession2 = build.openSession(true);
StudentMapper mapper2 = sqlSession2.getMapper(StudentMapper.class);
//新的会话查询就会从二级缓存中获取数据
Student stu2 = mapper2.findStudentByName("小花花");
System.out.println("第二次查出的对象"+stu1);
System.out.println("两次查询结果的学生对象是否是同一个:"+(stu1==stu2));
//关闭会话
sqlSession2.close();
rs.close();
}