浅谈Mybatis的一级缓存和二级缓存

MyBatis的缓存机制

缓存的引入

当我们大量执行重复的查询SQL语句的时候,会频繁的和数据库进行通信,会增加查询时间等影响用户体验的问题,可以通过缓存,以降低网络流量,使网站加载速度更快.

MyBatis的一级缓存

默认情况下,MyBatis只启用了本地的会话缓存,它仅仅对一个会话中的数据进行缓存。这也就是大家常说的MyBatis一级缓存,一级缓存的作用域是SqlSession。

第1种情况:同个session进行两次相同查询

代码验证

    //测试一级缓存
    @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语句的时候会在一级缓存中去获取,不用在与数据库进行通信,大大降低了查询速度.

第2种情况:同个session进行两次不同的查询。

** 代码演示**

//测试一级缓存
    @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();
    }

控制台输出
在这里插入图片描述

第3种情况:不同session,进行相同查询。

** 代码演示**

    //测试一级缓存
    @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();
    }

控制台输出
在这里插入图片描述

第4种情况:同个session,查询之后更新数据,再次查询相同的语句。

代码演示

    //测试一级缓存
    @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的二级缓存(全局缓存)

引入

MyBatis 一级缓存最大的共享范围就是一个SqlSession内部,那么如果多个 SqlSession 需要共享缓存,则需要开启二级缓存.

当二级缓存开启后,同一个命名空间(namespace) 所有的操作语句,都影响着一个共同的 cache,也就是二级缓存被多个 SqlSession 共享,是一个全局的变量。当开启缓存后,数据的查询执行的流程就是 二级缓存 -> 一级缓存 -> 数据库。

二级缓存开启条件
二级缓存默认是不开启的,需要手动开启二级缓存,实现二级缓存的时候,MyBatis要求返回的**POJO必须是可序列化的**。开启二级缓存的条件也是比较简单.

在MyBatis核心配置文件中配置开启二级缓存

<!-- 通知 MyBatis 框架开启二级缓存 -->
<settings>
  <setting name="cacheEnabled" value="true"/>
</settings>

在mapper配置文件中开启当前命名空间的查询结果存放在二级缓存中

    <!-- 表示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"
    />
  • 实体类对象要Serializable序列化
@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();
    }

控制台输出
在这里插入图片描述

图解查询语句执行流程
在这里插入图片描述

 

大多数时候,不是我们不努力,而是不知从何下手,我深知一份好的学习资料是多么的重要,我们通常会把大量的时间都浪费在找资源上。有时候就算有了视频资源,但是没有一个清晰的学习路线,我们依旧 会事倍功半,

海量资源,不用到处找,V薪关注【八戒程序猿】就够了。系统学习编程,并没有培训班那么贵。 」

  • 2
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值