MyBatis缓存机制

目录

文章目录

一、一级缓存

二、二级缓存

总结


一、一级缓存

MyBatis的一级缓存是SqlSession级别的缓存。如果同一个SqlSession对象多次执行完全相同的SQL语句时,在第一次执行完成后,MyBatis会将查询结果写入到一级缓存中,此后,如果程序没有执行插入、更新、删除操作,当第二次执行相同的查询语句时,MyBatis会直接读取一级缓存中的数据,而不会再去数据库查询,从而提高了数据库的查询效率。

根据图书id查询图书测试一级缓存

test类核心代码(通过id查询两次输出book信息)

    @Test
    public void findBookByIdTest1() {

        // 2.使用session1查询id为1的图书的信息
        Book book1 = sqlSession.selectOne("cn.edu.imust.mapper."
                + "BookMapper.findBookById", 1);
        // 3.输出查询结果信息
        System.out.println(book1.toString());
        // 4.使用session1查询id为1的图书的信息
        Book book2 = sqlSession.selectOne("cn.edu.imust.mapper."
                + "BookMapper.findBookById", 1);
        // 5.输出查询结果信息
        System.out.println(book2.toString());
    }

 通过下图观察,虽说用到了两次查询,但通过日志文件可看出只有一次通过数据库代码查询的结果,说明在第二次时调用的一级缓存的内容。而不是在去数据库中执行代码。提高了效率。

MyBatis缓存机制_第1张图片

 

MyBatis如何防止程序误读

当程序对数据库执行了插入、更新、删除操作后,MyBatis会清空一级缓存中的内容,以防止程序误读。MyBatis一级缓存被清空之后,再次使用SQL查询语句访问数据库时,MyBatis会重新访问数据库。例如上面的例子,首先查询id为1的图书信息,然后使用更新语句对数据库中的图书信息进行更改,更改之后,再次对id为1的图书信息进行查询时,MyBatis依然会从数据库中查询。

新增一个更新功能进行测试(先查询,然后修改数据,然后再查询)

 @Test
    public void findBookByIdTest2() {

        // 2.使用session查询id为1的图书的信息
        Book book1 = sqlSession.selectOne("cn.edu.imust.mapper."
                + "BookMapper.findBookById", 1);
        // 3.输出查询结果信息
        System.out.println(book1.toString());
        Book book2 = new Book();
        book2.setId(3);
        book2.setBookName("MySQL数据库入门");
        book2.setPrice(50.0);
        // 4.使用session更新id为3的图书的信息
        sqlSession.update("cn.edu.imust.mapper."
                + "BookMapper.updateBook", book2);

        Book book3 = sqlSession.selectOne("cn.edu.imust.mapper."
                + "BookMapper.findBookById", 1);
        // 5.输出查询结果信息
        System.out.println(book1.toString());

    }

通过效果图可看出,第一次查询访问了数据库,第二次进行更改操作也是访问了数据库更新了数据库的数据。第三次的话因为第二次执行了更新操作,所以数据库内容变化需要再次进行访问数据库查询数据。这样才不会出现误读数据。

MyBatis缓存机制_第2张图片 

二、二级缓存

相同的Mapper类,相同的SQL语句,如果SqlSession不同,则两个SqlSession查询数据库时,会查询数据库两次,这样也会降低数据库的查询效率。为了解决这个问题,就需要用到MyBatis的二级缓存。MyBatis的二级缓存是Mapper级别的缓存,与一级缓存相比,二级缓存的范围更大,多个SqlSession可以共用二级缓存,并且二级缓存可以自定义缓存资源。

在MyBatis中,一个Mapper.xml文件通常称为一个Mapper,MyBatis以namespace区分Mapper,如果多个SqlSession对象使用同一个Mapper的相同查询语句去操作数据库,在第一个SqlSession对象执行完后,MyBatis会将查询结果写入二级缓存,此后,如果程序没有执行插入、更新、删除操作,当第二个SqlSession对象执行相同的查询语句时,MyBatis会直接读取二级缓存中的数据。

与MyBatis的一级缓存不同的是,MyBatis的二级缓存需要手动开启,开启二级缓存通常要完成以下两个步骤

1.需要在MyBatis的核心配置mybatis-config.xml文件中通过元素开启二级缓存的全局配置。


        

2.修改映射文件BookMapper.xml,在映射文件的元素下追加编写元素开启当前Mapper的namespace的二级缓存。

MyBatis缓存机制_第3张图片

 测试核心代码(创建2个sqlsession,都读取一个方法)

/**
     * 根据id查询图书信息—-二级缓存
     */
    @Test
    public void findBookByIdTest3() {
        // 1.通过工具类生成SqlSession对象
        SqlSession session1 = MyBatisUtils.getSession();
        SqlSession session2 = MyBatisUtils.getSession();
        // 2.使用session1查询id为1的图书的信息
        Book book1 = session1.selectOne("cn.edu.imust.mapper."
                + "BookMapper.findBookById", 1);
        // 3.输出查询结果信息
        System.out.println(book1.toString());
        // 4.关闭SqlSession1
        session1.close();
        // 5.使用session2查询id为1的图书的信息
        Book book2 = session2.selectOne("cn.edu.imust.mapper."
                + "BookMapper.findBookById", 1);
        // 6.输出查询结果信息
        System.out.println(book2.toString());
        // 7.关闭SqlSession2
        session2.close();
    }

通过下图可看出当第一个SqlSession对象session1执行查询时,Cache Hit Ratio(缓存命中率)为0,程序发送了SQL语句;当第二个SqlSession对象session2执行相同的查询时,Cache Hit Ratio为0.5,程序没有发出SQL语句,这就说明,程序直接从二级缓存中获取了数据。

MyBatis缓存机制_第4张图片 

多个SqlSession在同一个Mapper中执行

在实际开发中,经常会遇到多个SqlSession在同一个Mapper中执行操作,例如,SqlSession1执行查询操作,SqlSession2执行插入、更新、删除操作,SqlSession3又执行和SqlSession1相同的查询操作。当SqlSession1执行查询操作时,程序会将查询结果写入MyBatis二级缓存,当SqlSession2对数据库执行了插入、更新、删除操作后,MyBatis会清空二级缓存中的内容,以防止程序误读。当SqlSession3执行和SqlSession1相同的查询操作时,MyBatis会重新访问数据库。

测试方法(通过3个sqlsession,进行查询,更新,再查询操作)

@Test
    public void findBookByIdTest4() {
        // 1.通过工具类生成SqlSession对象
        SqlSession session1 = MyBatisUtils.getSession();
        SqlSession session2 = MyBatisUtils.getSession();
        SqlSession session3 = MyBatisUtils.getSession();
        // 2.使用session1查询id为1的图书的信息
        Book book1 = session1.selectOne("cn.edu.imust.mapper."
                + "BookMapper.findBookById", 1);
        // 3.输出查询结果信息
        System.out.println(book1.toString());
        // 4.关闭SqlSession
        session1.close();
        Book book2 = new Book();
        book2.setId(2);
        book2.setBookName("Java Web程序开发进阶");
        book2.setPrice(45.0);
        // 5.使用session2更新id为2的图书的信息
        session2.update("cn.edu.imust.mapper."
                + "BookMapper.updateBook", book2);
        session2.commit();
        session2.close();
        // 6.使用session3查询id为1的图书的信息
        Book book3 = session3.selectOne("cn.edu.imust.mapper."
                + "BookMapper.findBookById", 1);
        // 7.输出查询结果信息
        System.out.println(book3.toString());
        
    }

 通过下图可看出第一次查询访问数据库,第二次更新操作也访问数据库,第三次查询时还是要进行访问数据库查询。

MyBatis缓存机制_第5张图片

补充:

Cache Hit Ratio(缓存命中率)

终端用户访问缓存时,如果在缓存中查找到了要被访问的数据,就叫做命中。如果缓存中没有查找到要被访问的数据,就是没有命中。当多次执行查询操作时,缓存命中次数与总的查询次数(缓存命中次数+缓存没有命中次数)的比,就叫作缓存命中率,即缓存命中率=缓存命中次数/总的查询次数。

 

总结

Mybatis缓存机制可大大提高查询效率,java代码访问数据库中最频繁的操作就是查询,所以可以有效地减轻数据库的工作量。对我们而言在学习Mybatis时了解这个缓存机制是必要的~

你可能感兴趣的:(后端知识点,mybatis,缓存,java,数据库,mysql)