为什么Mybatis一级和二级缓存都不建议使用?(三)

hexiaox810
发布于 2022-6-2 16:38
浏览
0收藏

 

测试二级缓存

二级缓存是基于namespace实现的,即一个mapper映射文件用一个缓存

在本实验中,id为1的学生名称初始化为点点。

「实验1」

测试二级缓存效果,不提交事务,sqlSession1查询完数据后,sqlSession2相同的查询是否会从缓存中获取数据。

为什么Mybatis一级和二级缓存都不建议使用?(三)-鸿蒙开发者社区

执行结果:

为什么Mybatis一级和二级缓存都不建议使用?(三)-鸿蒙开发者社区

我们可以看到,当sqlsession没有调用commit()方法时,二级缓存并没有起到作用。

「实验2」

测试二级缓存效果,当提交事务时,sqlSession1查询完数据后,sqlSession2相同的查询是否会从缓存中获取数据。  

为什么Mybatis一级和二级缓存都不建议使用?(三)-鸿蒙开发者社区为什么Mybatis一级和二级缓存都不建议使用?(三)-鸿蒙开发者社区

从图上可知,sqlsession2的查询,使用了缓存,缓存的命中率是0.5。

「实验3」

测试update操作是否会刷新该namespace下的二级缓存。

为什么Mybatis一级和二级缓存都不建议使用?(三)-鸿蒙开发者社区为什么Mybatis一级和二级缓存都不建议使用?(三)-鸿蒙开发者社区

  我们可以看到,在sqlSession3更新数据库,并提交事务后,sqlsession2的StudentMapper namespace下的查询走了数据库,没有走Cache。

「实验4」

验证MyBatis的二级缓存不适应用于映射文件中存在多表查询的情况。

为什么Mybatis一级和二级缓存都不建议使用?(三)-鸿蒙开发者社区

 getStudentByIdWithClassInfo的定义如下

为什么Mybatis一级和二级缓存都不建议使用?(三)-鸿蒙开发者社区

 通常我们会为每个单表创建单独的映射文件,由于MyBatis的二级缓存是基于namespace的,多表查询语句所在的namspace无法感应到其他namespace中的语句对多表查询中涉及的表进行的修改,引发脏数据问题。

为什么Mybatis一级和二级缓存都不建议使用?(三)-鸿蒙开发者社区

 执行结果: 

为什么Mybatis一级和二级缓存都不建议使用?(三)-鸿蒙开发者社区

在这个实验中,我们引入了两张新的表,一张class,一张classroom。class中保存了班级的id和班级名,classroom中保存了班级id和学生id。我们在StudentMapper中增加了一个查询方法getStudentByIdWithClassInfo,用于查询学生所在的班级,涉及到多表查询。在ClassMapper中添加了updateClassName,根据班级id更新班级名的操作。

当sqlsession1的studentmapper查询数据后,二级缓存生效。保存在StudentMapper的namespace下的cache中。当sqlSession3的classMapper的updateClassName方法对class表进行更新时,updateClassName不属于StudentMapper的namespace,所以StudentMapper下的cache没有感应到变化,没有刷新缓存。当StudentMapper中同样的查询再次发起时,从缓存中读取了脏数据。

「实验5」

为了解决实验4的问题呢,可以使用Cache ref,让ClassMapper引用StudenMapper命名空间,这样两个映射文件对应的SQL操作都使用的是同一块缓存了。

mapper文件中的配置如下

<cache-ref namespace="mapper.StudentMapper"/>

执行结果:

为什么Mybatis一级和二级缓存都不建议使用?(三)-鸿蒙开发者社区

 不过这样做的后果是,缓存的粒度变粗了,多个Mapper namespace下的所有操作都会对缓存使用造成影响。

总结
mybatis的一级缓存和二级缓存都是基于本地的,分布式环境下必然会出现脏读。

二级缓存可以通过实现Cache接口,来集中管理缓存,避免脏读,但是有一定的开发成本,并且在多表查询时,使用不当极有可能会出现脏数据

「除非对性能要求特别高,否则一级缓存和二级缓存都不建议使用」

 

 

文章转自公众号:Java识堂

分类
标签
已于2022-6-2 16:38:58修改
收藏
回复
举报
回复
    相关推荐