MyBatis 参数重复打印的bug

现象

最近有个需求,需要在mybatis对数据库进行写入操作的时候,根据条件对对象中的某个值进行置空,然后再进行写入,这样数据库中的值就会为空了。

根据网上查看的资料,选择在 StatementHandler 类执行 update 的时候进行对参数的拦截,

修改参数完毕后,再调用 statementHandler.getParameterHandler().setParameters 方法将修改后的值重新 set 进去,

此时出现了问题,发现mybatis在控制台log中打印出来的参数竟然多了一份,下面是临摹的情况,没有实际用update ,而是拦截的query方法,简化了一下逻辑:

这里我调用了3次 setParameters ,加上mybatis本身的一次,输出了4个 Parameters

在这里插入图片描述

实际上我的xml中只接收了一个参数,并且虽然log多打印了一些参数,实际对我的结果并无影响。

在这里插入图片描述

为什么会发生这个问题

问题就出在 StatementHandler.getParameterHandler().setParameters 这个方法上,这里是对本次数据库操作的参数进行赋值,

这过程中有一个 typeHandler.setParameter(ps, i + 1, value, jdbcType) 操作,
在这里插入图片描述

这里会根据相应的类型处理器,进行赋值,这个过程如下,typeHandler进行赋值操作,

这时候会调用 jdbcPreparedStatement.setXxx 方法,但是 mybatis对 PreparedStatement 做了一个拦截,

在这里插入图片描述

这个拦截就是日志记录类 PreparedStatementLogger ,当调用setXxx方法时,

在这里插入图片描述
Logger类会调用 setColumn 方法,这个setColumn方法就是记录本次入参情况,

在这里插入图片描述

最终调用 PreparedStatement.executeXXX方法时,本次代理将会根据条件决定要不要打印出参数等log,
在这里插入图片描述
而这个参数log,就是前面我们提到的setColumn方法中存入的 columnValues 属性,

在这里插入图片描述
可以看到,这个属性使用的是 ArrayList ,这也就说明了为什么会重复打印参数log出来,这意味着当你多次调用 StatementHandler.getParameterHandler().setParameters 这个方法时,columnValues 不会清除之前记录的参数,并且继续保存你这次重新set的参数进来。

在这里插入图片描述

如何解决

如果mybatis不使用ArrayList存值是否就可以避免这个问题,并且columnNamescolumnValues这两个值仅仅在打印log的时候使用,并没有在其他地方有使用到,

columnMap 刚好起到了,就算多次调用StatementHandler.getParameterHandler().setParameters方法,但是因为Map有过滤重复key的作用,然后使用columnMap中记录的值就可以防止参数重复打印的问题,

在这里插入图片描述

在这里插入图片描述
于是我给mybatis提了一个pr,借此来修复这个问题, pr链接:https://github.com/mybatis/mybatis-3/pull/3110

我的思路很简单,去除columnNamescolumnValues,仅保留columnMap,并且将columnMap改为LinkedHashMap类型,以此保证参数的顺序,经过测试这样做并没有发现什么问题,且保证了参数的正确输出。
在这里插入图片描述
不过很遗憾我的pr没有被合并,被关闭了,对方给的理由是不保证在拦截器中做出的一些操作对mybatis的运行产生一些的副作用,且给出友好提示,是否有其他的方式来解决我的需求问题。

重新判断问题

我们重新思考一下这个问题,问题出现在StatementHandler.getParameterHandler().setParameters 这里,我对参数重新赋值会导致这个问题,

那么我为何要重新赋值?有没有办法不调用这个StatementHandler.getParameterHandler().setParameters 方法?

我需要重新赋值的原因是因为在拦截StatementHandlerupdatequery方法时,mybatis自身已经调用过setParameters方法,

此时如果我不重新调用一下,单纯的修改parameterObject 自身,那么PreparedStatement.executeXXX设置的参数其实还是上次的,并不会因为我修改了parameterObject 而变化,

所以根据提示,我们有没有办法在mybatis自身执行setParameters前进行对parameterObject 的修改,这样我们在执行过程中,由mybatis来做这个赋值的事情,log就只会打印一次了。

最终解决

那么mybatis是何时自己进行setParameters的?答案在StatementHandler.parameterize中,

是的,它会在update或者query方法执行前,对参数进行处理,所以我们应当拦截这一步的操作,在这一步对参数进行处理,

在这里插入图片描述
修改后的拦截器,可以看到这里我们对StatementHandler.parameterize方法进行拦截处理,并且修改参数,此时log中输出的就是我们最后一次修改的参数。
在这里插入图片描述

当然mybatis还提供了其他的拦截点,例如不拦截StatementHandler类,我们直接到源头ParameterHandler.setParameters,拦截设置参数方法,

在这里,我们修改他的参数也是可以的,如下图:

在这里插入图片描述

最终结论

可以看到,我们只要不在拦截器中调用setParameters方法,就不会触发log的重复打印,因为mybatis的log记录类,使用ArrayList记录每次的setXX入参, 因此选好时机做相应的处理,就不会出现问题,在合适的拦截点做相应的事情,

MyBatis的参数记录可能也没有考虑过重复调用的问题,或者也许有其他的考量,总之我们了解这个问题的原因,并且做相应的规避即可。

演示与复现问题的demo都在:https://github.com/qiaomengnan16/mybatis-log-bug,欢迎指正。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/506977.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

什么品牌的护眼台灯比较好?收获好评的护眼台灯十大品牌推荐!

在这个数字时代,保护双眼的健康愈发受到人们的重视。而护眼台灯,作为守护视力的得力助手,其品牌选择至关重要。究竟什么品牌的护眼台灯比较好呢?今天,我将为大家推荐收获好评的护眼台灯十大品牌,这些品牌凭…

前端-css-01

1.CSS 长度单位和颜色设置 1.1CSS 中的长度单位 px 像素 em 字体大小的倍数(字体默认是16px) % 百分比 1.2CSS 中的颜色设置方式 1.2.1使用颜色名表示颜色 red、orange、yellow、green、cyan、blue、purple、pink、deeppink、skyblue、greenyellow .…

火鸟门户—团购秒杀

团购杀秒 简介 团购秒杀是一种基于社交裂变的电商模式,用户可以发起或参与拼团,以后续的价格购买商品。团购秒杀可以有效提升商品销量、引流新用户、增强用户粘性。 功能 团购:用户可以发起或参与拼团,以不同的价格购买商品。 秒杀…

docker部署修改主机网络

教学版教程:docker 部署教学版本-CSDN博客文章浏览阅读1.1k次,点赞23次,收藏18次。1)docker 部署mysql、redis、nginx ;2)docker compose一键单机部署;3)docker网络;4)dcocker swarn…

混合现实(MR)开发工具

混合现实(MR)开发工具是一系列软件和框架,它们使得开发者能够创建和优化能够在虚拟与现实世界之间无缝交互的应用程序。以下是一些在MR领域内广泛使用的开发工具。 1.Microsoft Mixed Reality Toolkit (MRTK) MRTK是一个跨平台的工具包&…

石煤酸浸提钒工艺-树脂

摘要:海普提钒树脂在使用中拥有更高交换容量、树脂处理量更大、吸附精度高,对钒的选择性更好,配合海普提钒离子交换富集纯化工艺设备,更好的保证了系统运行平稳性与可靠性。​​ #石煤酸浸提钒工艺-树脂 ​钒是一种重要的战略物资…

【小黑送书—第十八期】>>让工作自动化起来!无所不能的Python(文末送书)

随着我国企业数字化和信息化的深入,企业对办公自动化的效率和灵活性要求越来越高。Python作为一种开源的软件应用开发方式,通过提供强大丰富的库文件包,极大地简化了应用开发过程,降低了技术门槛。Python开发有哪些优势、挑战以及…

设备健康监测系统:保障设备安全与稳定运行!

前言 随着科技的不断发展,各种设备的使用也越来越广泛,从而带来了更多的设备管理和维护问题。设备健康监测系统应运而生,它是一种能够监测、分析和管理设备健康状况的系统,及时发现设备故障和降低维修成本。该系统通过实时监测设…

shell脚本发布docker springboot项目示例

docker、git、Maven、jdk8安装略过。 使git pull或者git push不需要输入密码操作方法 约定: 路径:/opt/springbootdemo, 项目:springbootdemo, 打包:springbootdemo.jar, docker容器名字&#x…

蓝桥杯刷题day13——自助餐【算法赛】

一、问题描述 食堂最近推出了自助取餐功能,可以通过盘子的形状自动计算费用。你参与到自助计算价格的项目工作中。视觉组的同学已经帮你通过图像识别把盘子图片转换为了字符串,你只需要计算具体的价格即可。 餐盘的费用如下表所示: 你将会得到n 个字符串,请按照价格表计算…

【Linux】动态库与静态库

👀樊梓慕:个人主页 🎥个人专栏:《C语言》《数据结构》《蓝桥杯试题》《LeetCode刷题笔记》《实训项目》《C》《Linux》《算法》 🌝每一个不曾起舞的日子,都是对生命的辜负 目录 前言 1.为什么要有库&…

数字人克隆系统源码部署,该如何选择源码厂家

短视频领域成2024年风口,数字化概念成为我们生活中不可或缺的一部分。在数字化的大潮中,有很多创业者开始瞄准了数字人源码部署这个商业风口项目。而许多企业开始考虑采用数字人系统进行业务拓展和服务升级,那么如何选择适合自己的数字人源码…

立白打造统一数据工作台,报表查看更方便,领导决策更高效!

数字化时代,数据就好比隐藏的宝藏,“寻宝人”若想精准、便捷地探索宝藏,就需要应用清晰、明确的藏宝图——数据门户。 今天,小编就带领大家,看看作为国内500强的日化品牌如何携手迅易科技搭建和应用统一数据门户&…

专题三_二分查找(1)

目录 704. 二分查找 解析 题解 34. 在排序数组中查找元素的第一个和最后一个位置 解析 题解 69. x 的平方根 解析 题解 704. 二分查找 704. 二分查找 - 力扣&#xff08;LeetCode&#xff09; 解析 题解 class Solution { public:int search(vector<int>& …

【考研数学】零基础120+备考计划(附资源分享)

大学四年没学过数学&#xff0c;那数学就等于零基础 如果想参加考研数学&#xff0c;并且想获得一个比较好的分数&#xff0c;那我建议&#xff0c;一定要从头开始学&#xff0c;从最简单的开始学&#xff01; 在考研的选择上&#xff0c;我们都知道&#xff0c;有数学一、数…

【C++】一篇文章带你深入了解string

目录 一. 为什么学习string&#xff1f;二、 标准库中的string2.1 string介绍2.2 string的常用接口说明2.2.1 string对象的常见构造2.2.1.1 [string()](https://legacy.cplusplus.com/reference/string/string/string/) ---- 无参构造函数2.2.1.2 [string(const char* s)](http…

Vue限制文本框显示字数,多余用...代替

1.在filters.js封装过滤器方法 import Vue from vue//设置只显示几个字符串&#xff0c;默认20个 Vue.filter(filterAmount, function(value, n) {if(!n) n 20;if(value && value.length > n) {value value.substring(0, n) ...;}return value;} )2.在main.js引…

JavaEE 初阶篇-深入了解多线程安全问题(指令重排序、解决内存可见性与等待通知机制)

&#x1f525;博客主页&#xff1a; 【小扳_-CSDN博客】 ❤感谢大家点赞&#x1f44d;收藏⭐评论✍ 文章目录 1.0 指令重排序概述 1.1 指令重排序主要分为两种类型 1.2 指令重排序所引发的问题 2.0 内存可见性概述 2.1 导致内存可见性问题主要涉及两个方面 2.2 解决内存可见性问…

【计算机考研】 408难吗?学到什么程度才能考130?

如果你是92科班&#xff0c;我觉得130是有机会的 。如果双非或者跨考&#xff0c;我觉得是很难的。可以关注一下可软和软微的复试通知。可以看到数学130以上的大有人在&#xff0c;408能考到130的寥寥无几。 而且从今年来看&#xff0c;对于基础还行&#xff0c;只用王道的我来…

LeetCode算法——数组/字符串篇

对刷过的算法进行总结&#xff0c;所用解法都是最符合我个人逻辑的&#xff0c;以后再刷的话就看这篇帖子了 # 代码随想录——数组理论基础 首先要知道数组在内存中的存储方式&#xff0c;这样才能真正理解数组相关的面试题 数组是存放在连续内存空间上的相同类型数据的集合 …