什么是二级缓存:
一级缓存是基于sqlsession级别, 当一个sqlsession会话结束, 一级缓存也就结束了.
定义一级缓存为局部缓存, 那么二级缓存就是全局全局缓存
二级缓存是基于mapper文件的namespace级别,也就是说多个sqlSession可以共享一个mapper中的二级缓存区域,并且如果两个mapper的namespace 相同,即使是两个mapper,那么这两个mapper中执行sql查询到的数据也将存在相同的二级缓存区域中。
会演示二级缓存生效/失效的场景
项目地址: https://gitee.com/xmaxm/test-code/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 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 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 queryWrapper = new LambdaQueryWrapper<>();queryWrapper.select(SysUser::getUsername, SysUser::getPhone, SysUser::getId);queryWrapper.last("limit 1");List 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());}
@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());}
UPDATE sys_user SET phone = #{phone} WHERE id = #{id};
上一篇:财政政策与货币政策
下一篇:maya 卡通草地制作方法笔记