• MyBatis缓存技术的认识(通俗易懂)


    引言

    在一个Web项目中,查询数据库中的操作算是一个非常常用的操作,但是有些数据会被经常性的查询,而每一次都去数据库中查询这些重复的数据,会很消耗数据库的资源,同时使得查询效率也很低,而 MyBatis 中就通过缓存技术来解决这样的问题,也就是说:将一些经常查询,并且不经常改变的,以及数据的正确对最后的结果影响不大的数据,放置在一个缓存容器中,当用户再次查询这些数据的时候,就不必再去数据库中查询,直接在缓存中提取就可以了

    注:缓存可以简单理解为存在于内存中的临时数据

    MyBatis 提供了 一级缓存和二级缓存两种形式

    • 一级缓存:它是 SqlSession 级别的缓存,SqlSession 类的实例对象中提供了一个 HashMap 的结构,可以用于存储缓存数据,当我们再次查询同一数据的时候,MyBatis 会先去 SqlSession 中查询,有的话,就直接调用
    • 二级缓存:是Mapper 级别的缓存,也就是说,如果多个 SqlSession 类的实例,去操作同一个Mapper配置文件中的SQL,这些实例对象可以共用二级缓存

    一级缓存

    (1) 基本阐述

    上面我们总的讲了一级缓存的原理,现在梳理一下它细节

    以一个通过 id 查询用户的例子来说

    • 第一次查询 id 为某个值的用户信息时,先去 SqlSesion 的一级缓存中去寻找,如果找到了,就直接用,如果没有找到就去数据库中去查,然后将查到的内容存到一级缓存区域
    • 但是,如果在下一次操作中,执行了 commit 操作,也就是执行了增删改的操作,一级缓存区域内的内容会被清空,这是为了保证缓存中的数据的有效性,避免脏读的产生

    (2) 程序演示

    演示前,我把需要准备的一些类或者表现贴出来

    User表

    CREATE TABLE USER (
     `id`			INT(11)NOT NULL AUTO_INCREMENT,
     `username` 	VARCHAR(32) NOT NULL COMMENT '用户名',
     `telephone`    VARCHAR(11) NOT NULL COMMENT '手机',
     `birthday`		DATETIME DEFAULT NULL COMMENT '生日',
     `gender`  		CHAR(1) DEFAULT NULL COMMENT '性别',
     `address` 		VARCHAR(256) DEFAULT NULL COMMENT '地址',
      PRIMARY KEY  (`id`)
    ) ENGINE=INNODB DEFAULT CHARSET=utf8;
    

    User实体类

    public class User implements Serializable {
        private Integer id;
        private String username;
        private String telephone;
        private Date birthday;
        private String gender;
        private String address;
    	...... 请补充 get set 方法
    }
    

    UserMapper接口

    public interface UserMapper {
        /**
         * 根据id查询用户信息
         * @param userId
         * @return
         */
        User findById(Integer userId);
    }
    

    UserMapper.xml

    <?xml version="1.0" encoding="UTF-8"?>
    <!DOCTYPE mapper
            PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
            "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
    <mapper namespace="cn.ideal.mapper.UserMapper">
        <!-- 根据id查询用户 -->
        <select id="findById" parameterType="INT" resultType="user">
           SELECT * FROM user where id = #{uid}
        </select>
    </mapper>
    

    测试方法

    /**
    * 测试查询所有
    */
    @Test
    public void testFirstLevelCache() {
        User user1 = userMapper.findById(16);
        System.out.println(user1);
        User user2 = userMapper.findById(16);
        System.out.println(user2);
        System.out.println(user1 == user2);
    }
    

    执行效果

    可以很明显的看到,当我们在同一个 sqlSession的情况下,当我们第一次查询 id 值为 16 的用户时,从数据库中确实查询到了数据,而第二次查询的时候,却没有任何日志的数据,而同时我们可以看到,通过输出语句,看到两个对象是完全相同的,这也就意味着,第二次查询不是从数据库查询出来的,而是从缓存中

    二级缓存

    (1) 基本阐述

    通过上面的简单认识,我们认识到一级缓存是基于同一个 SqlSession的,但是有时候由于方法封装的原因,或者在查询完,SqlSession 对象会关闭,一级缓存就清空了,会导致无法从中获取内容

    二级缓存的可以帮我们解决一级缓存无法使用的情况,前面已经说过二级缓存是Mapper 级别的缓存,多个SqlSession类的实例对象加载同一个Mapper配置文件,并执行其中SQL配置的收,他们就共享同一个 Mapper 缓存,执行流程也与一级缓存基本是一致的

    • 查询时,先去Mapper缓存区去找这个值,如果找不到,就去数据库查,然后将查询到的结果存储到缓存中,等下次使用
    • 当某个 SqlSession 类的实例对象执行了增删改操作时,二级缓存会被清空

    还依据刚开始准备的代码,我们直接写出其测试代码,看看在不同的 SqlSession 下,加载同一个Mapper的SQL是否会看到二级缓存的效果

    (2) 程序演示

    测试代码

    @Test
    public void testSecondLevelCache() {
        SqlSession sqlSession1 = factory.openSession();
        UserMapper mapper1 = sqlSession1.getMapper(UserMapper.class);
        User user1 = mapper1.findById(16);
        sqlSession1.close();
    
        SqlSession sqlSession2 = factory.openSession();
        UserMapper mapper2 = sqlSession2.getMapper(UserMapper.class);
        User user2 = mapper2.findById(16);
        sqlSession1.close();
    
        System.out.println(user1 == user2);
    }
    

    第一次执行效果

    看完测试代码,我们发现,SQL执行了两次,很显然,没有达到了我们的期望,那么是哪里不对呢?

    答案是,在MyBatis中一级缓存是默认开启的,而二级缓存则需要进行配置开启

    要开启二级缓存,需要进行两个操作步骤

    • ①:在总配置文件 SqlMapConfig.xml 中配置 setting属性
    • ②:在SQL映射文件中开启二级缓存

    通过官网的文档,可以看到,默认值就是true,所以,不配置也是可以的,不过我们还是先给出来

    修改 SqlMapConfig.xml

    <settings>
    	<setting name="cacheEnabled" value="true"/>
    </settings>
    

    修改 UserMapper.xml

    只需要在文件中添加一个 cache标签就可以了,非常简单

    <cache/>
    

    执行效果

    还有一个需要注意的地方,那就是我们最后做的判断 System.out.println(user1 == user2); 为什么的到的结果却是 false呢?

    这是因为,在二级缓存中,存入的是值,而不是对象,当需要使用的时候,会创建出新的用户,然后将值传入,所以这里是不等的

    不过使用二级缓存的时候,一定要谨慎,因为有时候不同的namespace下的 SQL配置中可能缓存着相同的数据,如我们上面的例子,UserMapper.xml 中有关于 user表的操作,但是如果在其他 Mmpper.xml 中仍然有针对 user 单表的操作,这会导致两方数据不一样,如果在我们 UserMapper.xml 进行了刷新缓存,但是另一个Mapper.xml 中可能仍有效,所以可能会出现错误

    结尾

    如果文章中有什么不足,欢迎大家留言交流,感谢朋友们的支持!

    如果能帮到你的话,那就来关注我吧!如果您更喜欢微信文章的阅读方式,可以关注我的公众号

    在这里的我们素不相识,却都在为了自己的梦而努力 ❤

    一个坚持推送原创开发技术文章的公众号:理想二旬不止

  • 相关阅读:
    LeetCode--Divide Two Integers
    mysql多实例安装与ssl认证
    ajax请求
    mysql5.6升级及mysql无密码登录
    mysql5.7密码设置
    BusyBox 添加 自定义命令小程序 (applet)
    分享9个常用的国外英文论文文献数据库
    arm linux 移植 gdb/gdbserver
    使用 mtd-utils 烧写Arm Linux 系统各个部分
    YUV图解 (YUV444, YUV422, YUV420, YV12, NV12, NV21)
  • 原文地址:https://www.cnblogs.com/ideal-20/p/12295410.html
Copyright © 2020-2023  润新知