记一次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中,也就不会触发一级缓存。

相关推荐
better_liang10 小时前
每日Java面试场景题知识点之-Java修饰符
java·访问控制·static·abstract·final·修饰符·企业级开发
rgeshfgreh10 小时前
Spring事务传播机制深度解析
java·前端·数据库
无名-CODING10 小时前
Java Spring 事务管理深度指南
java·数据库·spring
xiaolyuh12310 小时前
Spring MVC Bean 参数校验 @Validated
java·spring·mvc
想唱rap10 小时前
MYSQL在ubuntu下的安装
linux·数据库·mysql·ubuntu
蕨蕨学AI10 小时前
【Wolfram语言】45.2 真实数据集
java·数据库
The Sheep 202310 小时前
MongoDB与.Net6
数据库·mongodb
予枫的编程笔记10 小时前
【Java集合】深入浅出 Java HashMap:从链表到红黑树的“进化”之路
java·开发语言·数据结构·人工智能·链表·哈希算法
BryceBorder10 小时前
SCAU--数据库
数据库·oracle·dba
ohoy10 小时前
RedisTemplate 使用之Set
java·开发语言·redis