• 二、MyBatis 一二级缓存



    一级缓存【默认开启】

    • 订单表 与 会员表 是存在 一对多的关系,为了尽可能减少 join查询,进行了 分阶段查询,即先查询出 订单表,在根据 member_id字段 查询出 会员表,最后进行数据整合。
    • 如果 订单表中 存在重复的 member_id,就会出现很多没必要的重复查询。
    • 针对这种情况,MyBatis 通过 一级缓存 来实现,在同一次查询会话中,如果出现相同的语句及参数,就会从缓存中取出而不再查询数据库。
    • 一级缓存,只能作用于查询会话中,所以也叫做 会话缓存。

    1. 一级缓存的使用条件

    1. 必须是相同的 SQL 和 参数。
    2. 必须是相同的 会话。
    3. 必须是相同的 namespace,即同一个 Mapper接口。
    4. 必须是相同的 statement,即同一个 Mapper接口中的同一个方法。
    5. 查询语句中间,没有执行 session.clearCache()方法。
    6. 查询语句中间,没有执行 insert、update、delete方法(无论变动记录是否 与 缓存数据有无关系)。

    2. 一级缓存示例

    /**
     * 一级缓存。
     */
    @Test
    public void testL1Cache() {
        SqlSession sqlSession = sqlSessionFactory.openSession();
        UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
    
        System.out.println("1、------------------------------");
        User user = userMapper.getUserById(1);
    
        System.out.println("2、------------------------------");
        // 1、同一个Mapper。
        User user2 = userMapper.getUserById(1);
    
        System.out.println("3、------------------------------");
        // 2、同一个Session。
        User user3 = sqlSession.getMapper(UserMapper.class).getUserById(1);
    
        System.out.println("4、------------------------------");
        // 3、不同的Session。
        User user4 = sqlSessionFactory.openSession().getMapper(UserMapper.class).getUserById(1);
    
        System.out.println("------------------------------");
        System.out.println(user == user2);
        System.out.println(user == user3);
        System.out.println(user == user4);
        /*
        1、------------------------------
        SELECT * FROM `user` WHERE id = ?
        2、------------------------------
        3、------------------------------
        4、------------------------------
        SELECT * FROM `user` WHERE id = ?
        ------------------------------
        true
        true
        false
         */
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40

    3. 一级缓存源码解析


    3.1 缓存获取

    userMapper.getUserById(1)
    org.apache.ibatis.session.defaults.DefaultSqlSession.selectList(…) // 145

    org.apache.ibatis.executor.CachingExecutor.query(…) // 93

    org.apache.ibatis.executor.BaseExecutor.query(…) // 141

    org.apache.ibatis.cache.impl.PerpetualCache.getObject(.) // 54


    3.2 缓存存储

    userMapper.getUserById(1)
    org.apache.ibatis.session.defaults.DefaultSqlSession.selectList(…) // 145

    org.apache.ibatis.executor.CachingExecutor.query(…) // 93

    org.apache.ibatis.executor.BaseExecutor.query(…) // 141

    org.apache.ibatis.executor.BaseExecutor.queryFromDatabase(…) // 320

    org.apache.ibatis.cache.impl.PerpetualCache.putObject(…) // 49


    3.3 缓存清除

    clearCache作为入口,追踪一级缓存的实现PerpetualCache
    org.apache.ibatis.session.defaults.DefaultSqlSession.clearCache() // 305

    org.apache.ibatis.executor.CachingExecutor.clearLocalCache() // 160

    org.apache.ibatis.executor.BaseExecutor.clearLocalCache() // 263

    org.apache.ibatis.cache.impl.PerpetualCache.clear() // 64


    二级缓存

    • 业务系统中存在很多的 静态数据,如:字典表、菜单表、权限表等,这些数据的特性是不会轻易修改,但又是查询的热点数据。
    • 一级缓存 针对的是 同一个会话 当中 相同SQL,并不适合这种 热点数据的缓存 场景。
    • 为了解决这个问题,引入了二级缓存,它脱离于会话之外。

    1. 二级缓存的使用条件

    1. 当会话 提交 或 关闭 之后,才会填充二级缓存。
    2. 必须是 在同一个命名空间 之下。
    3. 必须是 相同的statement,即同一个Mapper接口中的同一个方法。
    4. 必须是 相同的SQL语句 和 参数。
    5. 如果 readWrite = true,实体对像 必须 实现 Serializable接口。

    2. 二级缓存的清除条件

    1. xml文件中 配置的update 不能清空 @CacheNamespace中的缓存数据。
    2. 只有 修改会话 提交之后,才会执行清空操作。
    3. 任何一种 增删改操作,都会清空整个 namespace中的缓存。

    3. 二级缓存示例

    @CacheNamespace
    //@CacheNamespace(
    //        implementation = PerpetualCache.class, // 缓存实现Cache接口的实现类。
    //        eviction = LruCache.class, // 缓存算法。
    //        flushInterval = 60000, // 刷新间隔时间(毫秒)。
    //        size = 1024, // 最大缓存引用对象。
    //        readWrite = true, // 是否可写。
    //        blocking = false // 是否阻塞。
    //)
    public interface UserMapper {
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    <mapper namespace="com.qs.mybatisfast.mapper.UserMapper">
    
    	<!-- 开启二级缓存。-->
        <cache/>
        
        <select id="getUserById" parameterType="int" resultType="com.qs.mybatisfast.pojo.User">
            SELECT *
            FROM `user`
            WHERE id = #{id}
        </select>
    </mapper>    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    /**
     * 二级缓存。
     */
    @Test
    public void testL2Cache() {
        SqlSession sqlSession = sqlSessionFactory.openSession();
        UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
    
        System.out.println("1、------------------------------");
        User user = userMapper.selectByTable("user", 1);
    
        System.out.println("2、------------------------------");
        // 1、同一个Mapper。
        User user2 = userMapper.selectByTable("user", 1);
    
        System.out.println("3、------------------------------");
        // 2、同一个Session。
        User user3 = sqlSession.getMapper(UserMapper.class).selectByTable("user", 1);
    
        System.out.println("4、------------------------------");
        // 3、不同的Session。
        User user4 = sqlSessionFactory.openSession().getMapper(UserMapper.class).selectByTable("user", 1);
    
        System.out.println("------------------------------");
        System.out.println(user == user2);
        System.out.println(user == user3);
        System.out.println(user == user4);
        /*
        1、------------------------------
        SELECT * FROM user WHERE id = ?
        2、------------------------------
        3、------------------------------
        4、------------------------------
        ------------------------------
        true
        true
        false
         */
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39

    4. 二级缓存源码解析


    4.1 缓存清除

    org.apache.ibatis.session.defaults.DefaultSqlSession.selectList(…) // 145

    org.apache.ibatis.executor.CachingExecutor.query(…) // 93

    org.apache.ibatis.executor.CachingExecutor.flushCacheIfRequired(.) // 164


    4.2 缓存获取

    org.apache.ibatis.cache.TransactionalCacheManager.getObject(…) // 34

    org.apache.ibatis.cache.decorators.TransactionalCache.getObject(.) // 66

    org.apache.ibatis.cache.decorators.SynchronizedCache.getObject(.) // 49

    org.apache.ibatis.cache.decorators.LoggingCache.getObject(.) // 55

    org.apache.ibatis.cache.decorators.SerializedCache.getObject(.) // 63

    org.apache.ibatis.cache.decorators.ScheduledCache.getObject(.) // 59

    org.apache.ibatis.cache.decorators.LruCache.getObject(.) // 72

    org.apache.ibatis.cache.impl.PerpetualCache.getObject(.) // 54


    4.3 缓存保存

    org.apache.ibatis.executor.CachingExecutor.close(.) // 55

    org.apache.ibatis.cache.TransactionalCacheManager.commit() // 42

    org.apache.ibatis.cache.decorators.TransactionalCache.commit() // 101

    org.apache.ibatis.cache.decorators.TransactionalCache.flushPendingEntries() // 120

    org.apache.ibatis.cache.decorators.SynchronizedCache.putObject(…) // 44

    org.apache.ibatis.cache.decorators.LoggingCache.putObject(…) // 50

    org.apache.ibatis.cache.decorators.SerializedCache.putObject(…) // 54

    org.apache.ibatis.cache.decorators.ScheduledCache.putObject(…) // 53

    org.apache.ibatis.cache.decorators.LruCache.putObject(…) // 66

    org.apache.ibatis.cache.impl.PerpetualCache.putObject(…) // 49


  • 相关阅读:
    【iOS】—— KVC
    Qt中利用QTextBrowser控件设计日志窗口
    阿里云服务器上CentOS 7.6使用rpm包安装MySQL 8.0.31
    [版本控制]——GitHub
    【计算机视觉 | 图像分割】arxiv 计算机视觉关于图像分割的学术速递(8 月 30 日论文合集)
    深入解析力扣176题:第二高的薪水(子查询与LIMIT详解及模拟面试问答)
    使用 Vite + 前端框架 (SolidJs,React,Svelte,Vue) 来开发 油猴脚本
    kafka安装
    App移动端测试(10)—— Monkey自定义脚本案例
    【C++】多态 ⑦ ( 多态机制实现原理 | 虚函数表概念 | 虚函数表工作机制 | vptr 指针 | 虚函数表运行时机制 | 虚函数与动态联编 )
  • 原文地址:https://blog.csdn.net/qq_38667427/article/details/125492045