Mybatis系列
2、全网最全MyBatis基于XML使用系列一:全局配置详解
3、全网最全MyBatis基于XML使用系列二:参数、返回结果处理
为什么要使用缓存
当用户多次查询相同的数据的时候,如果不需要缓存,那么每次都需要访问数据库,从而增加数据库的压力,当使用缓存之后,第一次将这些数据从数据库中查询,将查询的数据保存到缓存中,当用户再次查询相同的数据时,不用直接访问数据库进行查询,直接取缓存里面查询。这样可以减少网络连接和数据库查询带来的损耗,提高代码的查询效率,减少高并发访问带来的系统性能问题。
总的来说:查询一些不经常变化的数据,使用缓存可以提高查询效率,减少并发问题的发生。
Mybatis缓存主要分为一级缓存和二级缓存
一级缓存
Mybatis的一级缓存主要是SqlSession级别,默认是主动开启的。
基本概念
一般来说参数和sql完全一样的情况下,第一次执行SQL语句,使用SqlSession查询数据库,Mybatis会把结果集存放到缓存中并返回结果,如果第二次一直到第n次执行SQL语句进行查询,那么SqlSession就会查询当前第一次缓存的数据直接返回,而不会再次请求数据库查询。
基本验证
@Test
public void test01(){
SqlSession sqlSession=sqlSessionFactory.openSession();
try{
EmpMapper mapper=sqlSession.getMapper(EmpMapper.class);
List<Emp> list=mapper.selectAllEmp();
for(Emp emp:list){
System.out.println(emp);
}
System.out.println("=============");
List<Emp> list2=mapper.selectAllEmp();
for(Emp emp:list2){
System.out.println(emp);
}
}catch(Exception e){
e.printStackTrace();
}finally{
sqlSession.close();
}
分析:虽然在上面的代码中我们查询了两次,但最后只执行了一次数据库操作,这就是Mybatis提供的一级缓存在起作用了。因为一级缓存的存在,导致第二次查询id为1的记录时,并没有发出sql语句从数据库中查询数据,而是从一级缓存中查询。
一级缓存失效情况
开启多个SqlSession,缓存失效
一级缓存是sqlSession级别的缓存,如果在应用程序中只有开启了多个sqlsession,那么会造成缓存失效
@Test
public void test02() {
SqlSession sqlSession = sqlSessionFactory.openSession();
EmpMapper mapper = sqlSession.getMapper(EmpMapper.class);
List<Emp> list = mapper.selectAllEmp();
for (Emp emp : list) {
System.out.println(emp);
}
System.out.println("================================");
SqlSession sqlSession2 = sqlSessionFactory.openSession();
EmpMapper mapper2 = sqlSession2.getMapper(EmpMapper.class);
List<Emp> list2 = mapper2.selectAllEmp();
for (Emp emp : list2) {
System.out.println(emp);
}
sqlSession.close();
sqlSession2.close();
}
参数不一致,缓存失效
发送过程中发生了数据的修改,缓存失效
@Test
public void test03() {
SqlSession sqlSession = sqlSessionFactory.openSession();
EmpMapper mapper = sqlSession.getMapper(EmpMapper.class);
Emp empByEmpno = mapper.findEmpByEmpno(1111);
System.out.println(empByEmpno);
System.out.println("================================");
empByEmpno.setEname("zhangsan");
int i = mapper.updateEmp(empByEmpno);
System.out.println(i);
System.out.println("================================");
Emp empByEmpno1 = mapper.findEmpByEmpno(1111);
System.out.println(empByEmpno1);
sqlSession.close();
}
在两次查询期间,手动去清空缓存,缓存失效
@Test
public void test03() {
SqlSession sqlSession = sqlSessionFactory.openSession();
EmpMapper mapper = sqlSession.getMapper(EmpMapper.class);
Emp empByEmpno = mapper.findEmpByEmpno(1111);
System.out.println(empByEmpno);
System.out.println("================================");
System.out.println("手动清空缓存");
sqlSession.clearCache();
System.out.println("================================");
Emp empByEmpno1 = mapper.findEmpByEmpno(1111);
System.out.println(empByEmpno1);
sqlSession.close();
}
总结
特点:
- 1.默认就开启了,也可以关闭一级缓存 localCacheScope=STATEMENT
- 2.作用域:是基于sqlSession(默认),一次数据库操作会话。
- 3.缓存默认实现类PerpetualCache ,使用map进行存储的
- 4.查询完就会进行存储
- 5.先从二级缓存中获取,再从一级缓存中获取 * key==> sqlid+sql
失效情况:
- 1.不同的sqlSession会使一级缓存失效
- 2.同一个SqlSession,但是查询语句不一样
- 3.同一个SqlSession,查询语句一样,期间执行增删改操作
- 4.同一个SqlSession,查询语句一样,执行手动清除缓存
二级缓存
二级缓存是namespace级别的缓存,他比一级缓存更加底层,一般情况下Mybatis是默认不开启二级缓存的。
如果需要开启二级缓存那么则需要实现一下两个条件
- 实体类必须序列化
- 在xml配置文件中配置cache标签
基本实现
- 1、全局配置文件中添加如下配置:
<settings>
<!--因为cacheEnabled的取值默认就为true,所以这一步可以省略不配置。为true代表开启二级缓存;为false代表不开启二级缓存。-->
<setting name="cacheEnabled" value="true"/>
</settings>
- 2、配置EmpMapper.xml映射
<!--当前映射文件开启二级缓存-->
<cache></cache>
-
3、修改实体类必须要实现Serializable接口
-
4、测试实现
@Test
public void test04() {
SqlSession sqlSession = sqlSessionFactory.openSession();
SqlSession sqlSession2 = sqlSessionFactory.openSession();
EmpMapper mapper = sqlSession.getMapper(EmpMapper.class);
EmpMapper mapper2 = sqlSession2.getMapper(EmpMapper.class);
Emp empByEmpno = mapper.findEmpByEmpno(1111);
System.out.println(empByEmpno);
sqlSession.close();
Emp empByEmpno1 = mapper2.findEmpByEmpno(1111);
System.out.println(empByEmpno1);
sqlSession2.close();
}
缓存标签属性
-
eviction:表示缓存回收策略,默认是LRU
-
LRU:最近最少使用的,移除最长时间不被使用的对象
-
FIFO:先进先出,按照对象进入缓存的顺序来移除
-
SOFT:软引用,移除基于垃圾回收器状态和软引用规则的对象
-
WEAK:弱引用,更积极地移除基于垃圾收集器状态和弱引用规则的对象
-
-
flushInternal:刷新间隔,单位毫秒
默认情况是不设置,也就是没有刷新间隔,缓存仅仅调用语句时刷新
-
size:引用数目,正整数
代表缓存最多可以存储多少个对象,太大容易导致内存溢出
-
readonly:只读,true/false
-
true:只读缓存,会给所有调用这返回缓存对象的相同实例,因此这些对象不能被修改。
-
false:读写缓存,会返回缓存对象的拷贝(序列化实现),这种方式比较安全,默认值
-
@Test
public void test05() {
SqlSession sqlSession = sqlSessionFactory.openSession();
EmpMapper mapper = sqlSession.getMapper(EmpMapper.class);
Emp empByEmpno = mapper.findEmpByEmpno(1111);
System.out.println(empByEmpno);
sqlSession.close();
SqlSession sqlSession2 = sqlSessionFactory.openSession();
EmpMapper mapper2 = sqlSession2.getMapper(EmpMapper.class);
Emp empByEmpno2 = mapper2.findEmpByEmpno(1111);
System.out.println(empByEmpno2);
Emp empByEmpno3 = mapper2.findEmpByEmpno(1111);
System.out.println(empByEmpno3);
Emp empByEmpno4 = mapper2.findEmpByEmpno(7369);
System.out.println(empByEmpno4);
Emp empByEmpno5 = mapper2.findEmpByEmpno(7369);
System.out.println(empByEmpno5);
sqlSession2.close();
}
总结
特性:
- 1.默认开启了,没有实现
- 2.作用域:基于全局范围,应用级别。
- 3.缓存默认实现类PerpetualCache ,使用map进行存储的但是二级缓存根据不同的mapper命名空间多包了一层map
- 4.事务提交的时候(sqlSession关闭)
- 5.先从二级缓存中获取,再从一级缓存中获取 *
实现:
-
1.开启二级缓存
<setting name="cacheEnabled" value="true"/> -
2.在需要使用到二级缓存的映射文件中加入,基于Mapper映射文件来实现缓存的,基于Mapper映射文件的命名空间来存储的
-
3.在需要使用到二级缓存的javaBean中实现序列化接口implements Serializable
- 配置成功就会出现缓存命中率 同一个sqlId: 从缓存中拿出的次数/查询总次数
失效:
-
1.同一个命名空间进行了增删改的操作,会导致二级缓存失效
但是如果不想失效:可以将SQL的flushCache 这是为false,但是要慎重设置,因为会造成数据脏读问题,除非你能保证查询的数据永远不会执行增删改
-
2.让查询不缓存数据到二级缓存中useCache="false"
-
3.如果希望其他mapper映射文件的命名空间执行了增删改清空另外的命名空间就可以设置:
<cache-ref namespace="com.mybatis.mapper.DeptMapper"/>