• Mybatis的二级缓存 (默认方式)



    前置

    什么是二级缓存:

    一级缓存是基于sqlsession级别, 当一个sqlsession会话结束, 一级缓存也就结束了.
    定义一级缓存为局部缓存, 那么二级缓存就是全局全局缓存
    二级缓存是基于mapper文件的namespace级别,也就是说多个sqlSession可以共享一个mapper中的二级缓存区域,并且如果两个mapper的namespace 相同,即使是两个mapper,那么这两个mapper中执行sql查询到的数据也将存在相同的二级缓存区域中。

    会演示二级缓存生效/失效的场景
    项目地址: https://gitee.com/xmaxm/chaim-code-template/blob/master/chaim-cache/chaim-mybatis-cache/chaim-mybatis-cache-two/README.md

    前置配置:

    二级缓存(全局缓存)(namespace级别)
    第一步需配置: mybatis-plus.configuration.cache-enabled: true 默认true
    第二步: 对应entity需要实现 Serializable
    第三步: (对应的 mapper 添加 @CacheNamespace->可配缓存参数, xml 添加 标签) 或者 (mapper 添加 @CacheNamespaceRef, xml 添加 标签->可配缓存参数)
    缓存可配置参数: https://mybatis.org/mybatis-3/zh/sqlmap-xml.html#cache

    注意点

    @CacheNamespace(blocking = true) 属性可避免瞬时流量涌入直接打入数据库. 自定义二级缓存(后续文章会有介绍)方式是不支持该属性的, 需要考虑自己实现

    源码部分

    感觉要是把源码过一遍, 得从新起一篇文章才行, 后面有需要在写, 偷个懒吧, 哈哈哈哈哈!

    源码入口: org.apache.ibatis.mapping.CacheBuilder#build
    关键类: org.apache.ibatis.cache.Cache
    默认实现: org.apache.ibatis.cache.impl.PerpetualCache
    默认淘汰策略: org.apache.ibatis.cache.decorators.LruCache

    相关缓存文章

    Mybatis的一级缓存
    Mybatis的二级缓存 (默认方式)
    Mybatis的二级缓存 (Redis方式)
    Mybatis的二级缓存 (ehcache方式)


    生效


    场景一

    测试二级缓存生效: 按前置描述配置
    使用mybatis plus方法

    public void queryingLevelCache() {
        LambdaQueryWrapper<SysUser> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.select(SysUser::getUsername, SysUser::getPhone, SysUser::getId);
        queryWrapper.last("limit 1");
        SysUser sysUsers = sysUserMapper.selectOne(queryWrapper);
        log.info("查询成功, 观察日志, id: {}", sysUsers.getId());
    
        sqlSession.clearCache();
    
        SysUser user = sysUserMapper.selectOne(queryWrapper);
        log.info("查询成功, 观察日志, id: {}", user.getId());
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    在这里插入图片描述


    场景二

    测试二级缓存生效: 按前置描述配置
    使用自定义SQL

    public void queryingLevelCache(Integer integer) {
        SysUser sysUsers = sysUserMapper.selectHandwritingSql();
        log.info("查询成功, 观察日志, id: {}", sysUsers.getId());
    
        SysUser user = sysUserMapper.selectHandwritingSql();
        log.info("查询成功, 观察日志, id: {}", user.getId());
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    在这里插入图片描述


    失效


    场景一

    测试二级缓存失效: 添加@Transactional, 使其在同一个 SqlSession, 然后手动清除缓存

    @Transactional(rollbackFor = Exception.class)
    public void queryingLevelCacheFail() {
        LambdaQueryWrapper<SysUser> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.select(SysUser::getUsername, SysUser::getPhone, SysUser::getId);
        queryWrapper.last("limit 1");
        SysUser sysUsers = sysUserMapper.selectOne(queryWrapper);
        log.info("查询成功, 观察日志, id: {}", sysUsers.getId());
    
        sqlSession.clearCache();
    
        SysUser user = sysUserMapper.selectOne(queryWrapper);
        log.info("查询成功, 观察日志, id: {}", user.getId());
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    在这里插入图片描述


    场景二

    测试二级缓存失效: 当两次查询的方式不一样, 使用mybatis的方法, 以及自定义SQL
    同理, 当查询的条件以及查询的内容不一致时也会失效

    public void queryingLevelCacheFail(Integer integer) {
        LambdaQueryWrapper<SysUser> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.select(SysUser::getUsername, SysUser::getPhone, SysUser::getId);
        queryWrapper.last("limit 1");
        List<SysUser> sysUsers = sysUserMapper.selectList(queryWrapper);
        log.info("查询成功, 观察日志, id: {}", sysUsers.size());
    
        SysUser user = sysUserMapper.selectHandwritingSql();
        log.info("查询成功, 观察日志, id: {}", user.getId());
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    在这里插入图片描述


    场景三

    测试二级缓存失效: 当两次查询存在之间, 存在增删改的情况

    public void queryingLevelCacheFail(String string) {
        SysUser sysUsers = sysUserMapper.selectHandwritingSql();
        log.info("查询成功, 观察日志, id: {}", sysUsers.getId());
    
        SysUser sysUser = SysUser.builder()
                .username("潇潇")
                .email("gmail.com")
                .phone("000123")
                .password("123456")
                .sex(1)
                .state(0)
                .salt(1234)
                .build();
        sysUserMapper.insert(sysUser);
        log.info("观察新增日志, id: {}", sysUsers.getPassword());
    
        SysUser user = sysUserMapper.selectHandwritingSql();
        log.info("查询成功, 观察日志, id: {}", user.getId());
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    在这里插入图片描述


    场景四

    测试二级缓存失效:
    xml 的标签指定 flushCache=“true”
    注解方式SQL配置: @Options(flushCache = Options.FlushCachePolicy.TRUE)

    同理还可以全局配置: 禁用mybatis一级缓存: mybatis-plus.configuration.cache-enabled: false. 默认开始 true

    public void queryingLevelCacheFail(Boolean bol) {
        SysUser sysUsers = sysUserMapper.selectHandwritingSqlFail();
        log.info("查询成功, 观察日志, id: {}", sysUsers.getId());
    
        SysUser user = sysUserMapper.selectHandwritingSqlFail();
        log.info("查询成功, 观察日志, id: {}", user.getId());
    
        log.info("-----------自义定SQL的两种失效方式-----------------");
    
        sysUsers = sysUserMapper.selectHandwritingSqlFail2();
        log.info("查询成功, 观察日志, id: {}", sysUsers.getId());
    
        user = sysUserMapper.selectHandwritingSqlFail2();
        log.info("查询成功, 观察日志, id: {}", user.getId());
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    <!-- flushCache默认false. true: 每次查询走数据库查询(SQL的二级缓存失效) -->
     <select id="selectHandwritingSqlFail" resultType="com.chaim.mybatis.cache.two.entitys.SysUser" flushCache="true">
         SELECT username,phone,id FROM sys_user limit 1
     </select>
    
    • 1
    • 2
    • 3
    • 4
    @Options(flushCache = Options.FlushCachePolicy.TRUE)
    @Select("SELECT username,phone,id FROM sys_user limit 1")
    SysUser selectHandwritingSqlFail2();
    
    • 1
    • 2
    • 3

    在这里插入图片描述
    在这里插入图片描述


    脏数据场景

    脏数据: 前提开启二级缓存. 在两次查询之间, 做INSERT UPDATE DELETE配置其标签: flushCache=“false”, 不清空缓存,
    导致第二条SQL走二级缓存获取的数据还是之前缓存的数据

        public void queryingLevelCacheError() {
            SysUser sysUser = sysUserMapper.selectHandwritingSql();
            log.info("查询成功, 观察日志, id: {}", sysUser.toString());
    
            sysUser.setPhone("999090912");
            sysUserMapper.updateHandwritingSql(sysUser);
            log.info("观察更新日志, id: {}", sysUser.getPassword());
    
            // 由于updateHandwritingSql配置不清除缓存, user的数据还是之前缓存数据(脏数据)
            SysUser user = sysUserMapper.selectHandwritingSql();
            log.info("查询成功, 观察日志, id: {}", user.toString());
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
     <!-- flushCache默认true.
     true: 会导致本地缓存和二级缓存被清空
     false: 不会清空本地缓存和二级缓存, 即缓存的数据还是之前的(脏读) -->
     <update id="updateHandwritingSql" flushCache="false">
         UPDATE sys_user SET phone = #{phone} WHERE id = #{id};
     </update>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    在这里插入图片描述

  • 相关阅读:
    面试经验二
    四种bean拷贝工具对比
    1.1.8 外键
    ubuntu安装PhotoPrism,并开启安卓照片同步
    JAVA计算机毕业设计信管专业毕业生就业管理信息系统Mybatis+源码+数据库+lw文档+系统+调试部署
    【C++11新特性】=default 和 =delete
    Windows资源管理器已停止工作的解决方法
    【CANoe】文件处理_hex文件读取解析
    Linux速成命令
    Python之并发编程
  • 原文地址:https://blog.csdn.net/qq_38637558/article/details/127924334