0
点赞
收藏
分享

微信扫一扫

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


目录

  • ​​前置​​
  • ​​生效​​
  • ​​场景一​​
  • ​​场景二​​
  • ​​失效​​
  • ​​场景一​​
  • ​​场景二​​
  • ​​场景三​​
  • ​​场景四​​
  • ​​脏数据场景​​

前置

什么是二级缓存:

一级缓存是基于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<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的二级缓存 (默认方式)_mybatis

场景二

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

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

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

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

失效

场景一

测试二级缓存失效: 添加@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的二级缓存 (默认方式)_java_03

场景二

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

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

场景三

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

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

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

场景四

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

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


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

脏数据场景

脏数据: 前提开启二级缓存. 在两次查询之间, 做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>

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


举报

相关推荐

0 条评论