记一次MybatisPlus一级缓存导致的DB对象取值逻辑错误

记一次MybatisPlus一级缓存导致的DB对象取值逻辑错误

MybatisPlus我想每个做Springboot应用开发的朋友都在项目中用过,支持原生sql的编写,也提供Wrapper构造sql进行查询,还有许多封装的DAO层的service方法,对单表CRUD的支撑非常良好。Mybatis的一二级缓存大家肯定都有所耳闻,默认是开启一级缓存的,即单个sqlSession级别的缓存。可以理解为在同一个事务下,在没有执行额外增删改的情况下,相同的查询语句,从第二次开始会直接从缓存中拿第一次的查询结果。二级缓存是跨sqlSession级别的缓存,默认不开启。

场景描述

在近期的一次项目线上问题排除中,发现有两个物料计算出来放置的坐标点和预期的有所偏差,仔细核对代码后,发现逻辑无误,且通过Springboot单元测试,也能返回正确的结果。

后续在运行日志比对中,发现有一处查询物料信息的语句,没有输出对应的日志,这里首先确认了该表是开启SQL语句输出的(可以针对不同的mapper设置不同的日志输出级别,SQL输出日志级别为DEBUG,将指定mapper设置为INFO及以上级别时,就不会打印出来),那就基本可以确认是触发了Mybatis的一级缓存。

问题分析

关于Mybatis的一级缓存,可以先思考一下自己的认知在哪个程度。

缓存存在哪儿的?

第一次查询之后对象修改后是否影响第二次查询的结果?

第一次查询之后对象修改并更新到数据库后是否会触发第二次查询的一级缓存?

为什么DEBUG的时候会得出正确的结果?


其实关于这个一级缓存,在之前的项目中也遇到过类似的问题,在编码中经常会忽略这个问题,现场排查也不太好定位。首先可以确认的是第二次查询触发的一级缓存,会直接返回第一次查询结果的相同对象,意思就是第一次查询出来的结果,做了字段的set操作,是会影响到第二次查询的使用的。

我这里遇到的问题就是物料有理论宽度和视觉识别宽度,为了保证使用宽度的准确性,我会先判断有没有视觉识别宽度(相对更加准确),如果有则取二者的较大者作为物料物料宽度作为后续逻辑的输入,本身这个逻辑没啥问题,错就错在我把后续判断出来的这个有效宽度set到了原对象的理论宽度字段上,因为后续逻辑是其它同事开发的,用到的就是这个字段。

这儿的解决方案可以是将原DB查询出来的数据不做改动,深拷贝一个对象后,赋值,然后传到后续的方法中,从而不影响后续其它地方触发一级缓存的查询结果。抛开这里的这个场景,我们其它地方用到需要做字段更新,又不提交到数据库时,是否可以考虑通过DTO对象作参数或者是方法间的传输,不更新POJO时,尽量不做数据库表映射字段的set更新。

再说回为什么DEBUG的时候没有测出来的问题,很简单,因为之前DEBUG的测试方法没有标注事务注解,导致写的测试方法没有处于同一个sqlSession中,也就不会触发一级缓存。

相关推荐
独自破碎E11 小时前
LCR_019_验证回文串II
java·开发语言
坚持就完事了11 小时前
Java中的一些关键字
java·开发语言
墨黎芜11 小时前
SQL Server从入门到精通——C#与数据库
数据库·学习·信息可视化
爱学习的阿磊11 小时前
持续集成/持续部署(CI/CD) for Python
jvm·数据库·python
一个响当当的名号11 小时前
lectrue10 排序和聚合算法
数据库
hamawari11 小时前
SQL语法
数据库·sql·oracle
陌上丨11 小时前
Redis内存使用率在95%以上,请问是什么原因?如何解决?
数据库·redis·缓存
寄存器漫游者11 小时前
数据结构 C语言 顺序栈
java·c语言·数据结构
heartbeat..11 小时前
Redis 性能优化全指南:从基础配置到架构升级
java·redis·性能优化·架构