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());
}

在这里插入图片描述


场景二

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

public void queryingLevelCache(Integer integer) {
    SysUser sysUsers = sysUserMapper.selectHandwritingSql();
    log.info("查询成功, 观察日志, id: {}", sysUsers.getId());

    SysUser user = sysUserMapper.selectHandwritingSql();
    log.info("查询成功, 观察日志, id: {}", user.getId());
}

在这里插入图片描述


失效


场景一

测试二级缓存失效: 添加@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());
}

在这里插入图片描述


场景二

测试二级缓存失效: 当两次查询的方式不一样, 使用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());
}

在这里插入图片描述


场景三

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

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());
}

在这里插入图片描述


场景四

测试二级缓存失效:
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());

}
<!-- 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>
@Options(flushCache = Options.FlushCachePolicy.TRUE)
@Select("SELECT username,phone,id FROM sys_user limit 1")
SysUser selectHandwritingSqlFail2();

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


脏数据场景

脏数据: 前提开启二级缓存. 在两次查询之间, 做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());
    }
 <!-- flushCache默认true.
 true: 会导致本地缓存和二级缓存被清空
 false: 不会清空本地缓存和二级缓存, 即缓存的数据还是之前的(脏读) -->
 <update id="updateHandwritingSql" flushCache="false">
     UPDATE sys_user SET phone = #{phone} WHERE id = #{id};
 </update>

在这里插入图片描述

  • 3
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值