• MyBatis缓存机制


    一、一级缓存

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

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

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

    1. @Test
    2. public void findBookByIdTest1() {
    3. // 2.使用session1查询id为1的图书的信息
    4. Book book1 = sqlSession.selectOne("cn.edu.imust.mapper."
    5. + "BookMapper.findBookById", 1);
    6. // 3.输出查询结果信息
    7. System.out.println(book1.toString());
    8. // 4.使用session1查询id为1的图书的信息
    9. Book book2 = sqlSession.selectOne("cn.edu.imust.mapper."
    10. + "BookMapper.findBookById", 1);
    11. // 5.输出查询结果信息
    12. System.out.println(book2.toString());
    13. }

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

     

    MyBatis如何防止程序误读

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

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

    1. @Test
    2. public void findBookByIdTest2() {
    3. // 2.使用session查询id为1的图书的信息
    4. Book book1 = sqlSession.selectOne("cn.edu.imust.mapper."
    5. + "BookMapper.findBookById", 1);
    6. // 3.输出查询结果信息
    7. System.out.println(book1.toString());
    8. Book book2 = new Book();
    9. book2.setId(3);
    10. book2.setBookName("MySQL数据库入门");
    11. book2.setPrice(50.0);
    12. // 4.使用session更新id为3的图书的信息
    13. sqlSession.update("cn.edu.imust.mapper."
    14. + "BookMapper.updateBook", book2);
    15. Book book3 = sqlSession.selectOne("cn.edu.imust.mapper."
    16. + "BookMapper.findBookById", 1);
    17. // 5.输出查询结果信息
    18. System.out.println(book1.toString());
    19. }

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

     

    二、二级缓存

    相同的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文件中通过元素开启二级缓存的全局配置。

    1. <setting name="cacheEnabled" value="true"/>

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

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

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

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

     

    多个SqlSession在同一个Mapper中执行

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

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

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

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

    补充:

    Cache Hit Ratio(缓存命中率)

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

     

    总结

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

  • 相关阅读:
    Tomcat日志分割
    MyBatis_MyBatis之查询返回对象集合
    百分点科技受邀参加“一带一路”国际合作高峰论坛
    C++语法基础(1)——编程入门
    R语言结构方程模型(SEM)在生态学领域中的实践应用
    积加(跨境ERP)与金蝶云星空单据集成对接
    手机裁剪音频的软件有哪些?来看看这几个吧
    PIE-engine 教程 ——map()映射函数和for循环函数的综合应用NDVI和NDWI计算北京市各区面积
    前端经典面试题 | 闭包的作用和原理
    dvwa命令执行漏洞分析
  • 原文地址:https://blog.csdn.net/weixin_52258054/article/details/127695667