前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >全网最全MyBatis基于XML使用系列四:缓存

全网最全MyBatis基于XML使用系列四:缓存

作者头像
玖柒的小窝
发布2021-12-10 22:23:26
发布2021-12-10 22:23:26
24500
代码可运行
举报
文章被收录于专栏:各类技术文章~各类技术文章~
运行总次数:0
代码可运行

为什么要使用缓存

当用户多次查询相同的数据的时候,如果不需要缓存,那么每次都需要访问数据库,从而增加数据库的压力,当使用缓存之后,第一次将这些数据从数据库中查询,将查询的数据保存到缓存中,当用户再次查询相同的数据时,不用直接访问数据库进行查询,直接取缓存里面查询。这样可以减少网络连接和数据库查询带来的损耗,提高代码的查询效率,减少高并发访问带来的系统性能问题。

总的来说:查询一些不经常变化的数据,使用缓存可以提高查询效率,减少并发问题的发生。

Mybatis缓存主要分为一级缓存和二级缓存

一级缓存

Mybatis的一级缓存主要是SqlSession级别,默认是主动开启的。

基本概念

一般来说参数和sql完全一样的情况下,第一次执行SQL语句,使用SqlSession查询数据库,Mybatis会把结果集存放到缓存中并返回结果,如果第二次一直到第n次执行SQL语句进行查询,那么SqlSession就会查询当前第一次缓存的数据直接返回,而不会再次请求数据库查询。

基本验证

代码语言:javascript
代码运行次数:0
运行
复制
@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,那么会造成缓存失效

代码语言:javascript
代码运行次数:0
运行
复制
@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();
}
复制代码
参数不一致,缓存失效
发送过程中发生了数据的修改,缓存失效
代码语言:javascript
代码运行次数:0
运行
复制
@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();
}
复制代码
在两次查询期间,手动去清空缓存,缓存失效
代码语言:javascript
代码运行次数:0
运行
复制
@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、全局配置文件中添加如下配置:
代码语言:javascript
代码运行次数:0
运行
复制
<settings>
<!--因为cacheEnabled的取值默认就为true,所以这一步可以省略不配置。为true代表开启二级缓存;为false代表不开启二级缓存。-->
    <setting name="cacheEnabled" value="true"/>
</settings>
复制代码
  • 2、配置EmpMapper.xml映射
代码语言:javascript
代码运行次数:0
运行
复制
<!--当前映射文件开启二级缓存-->
<cache></cache>
复制代码
  • 3、修改实体类必须要实现Serializable接口
  • 4、测试实现
代码语言:javascript
代码运行次数:0
运行
复制
@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:读写缓存,会返回缓存对象的拷贝(序列化实现),这种方式比较安全,默认值
代码语言:javascript
代码运行次数:0
运行
复制
@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"/>

本文系转载,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文系转载前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 为什么要使用缓存
  • 一级缓存
    • 基本概念
    • 基本验证
    • 一级缓存失效情况
      • 开启多个SqlSession,缓存失效
      • 参数不一致,缓存失效
      • 发送过程中发生了数据的修改,缓存失效
      • 在两次查询期间,手动去清空缓存,缓存失效
    • 总结
  • 二级缓存
    • 基本实现
    • 缓存标签属性
    • 总结
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档