当前位置: 首页 > news >正文

解决 MyBatis 中空字符串与数字比较引发的条件判断错误

问题复现

假设你在 MyBatis 的 XML 配置中使用了如下代码:

<if test="isCollect != null"><choose><when test="isCollect == 1">AND exists(select 1 from file_table imgfile2 where task.IMAGE_SEQ=imgfile2.IMAGE_SEQ and imgfile2.DOWNLOAD_STATUS = 1)</when><when test="isCollect == 0">AND not exists(select 1 from file_table imgfile2 where task.IMAGE_SEQ=imgfile2.IMAGE_SEQ and imgfile2.DOWNLOAD_STATUS = 1)</when></choose>
</if>

在这段代码中,通过 <choose> 标签对 isCollect 的值进行判断。如果 isCollect 的值为 1,则执行一个 exists 查询;如果 isCollect 的值为 0,则执行一个 not exists 查询。然而,实际运行时,当 isCollect空字符串 ("") 时,代码却会意外地执行到 test="isCollect == 0" 这一条件。
具体分析后,得出如果传入的 isCollect 是空字符串 "",由于 OGNL 类型转换的原因,空字符串会被转换为 0,导致条件判断意外地返回 true,从而执行 SQL 分支。本文将详细解析这个问题的根本原因,并提供有效的解决方案。

1. MyBatis 条件判断的执行流程

在 MyBatis 中,<if test="..."> 标签的条件表达式通过 OGNL(Object-Graph Navigation Language)引擎来解析。OGNL 可以动态地访问对象的属性,并执行表达式。MyBatis 将 test 属性中的表达式传递给 OGNL 引擎进行解析和计算,判断条件是否成立。

关键流程:
  • test 表达式解析test="isCollect == 0" 中的 isCollect == 0 会被 OGNL 解析并执行。
  • OGNL 类型转换:OGNL 在比较值时,会根据目标类型自动进行类型转换。例如,空字符串 "" 会被转换为 0(数字),导致条件 test="isCollect == 0" 被错误地评估为 true

2. 源码分析

要理解为什么空字符串 "" 被错误地转换为 0,我们需要查看 MyBatis 3.5.10 中的关键源码,特别是 OGNL 引擎如何处理这种类型转换。

a. IfSqlNode

IfSqlNode 负责解析 <if test="..."> 标签中的条件表达式。它会将 test 中的表达式交给 OGNL 引擎进行解析,然后根据条件结果决定是否生成 SQL 片段。

  • 源码路径org.apache.ibatis.scripting.xmltags.IfSqlNode
  • 主要方法apply(DynamicContext context)
# org.apache.ibatis.scripting.xmltags.IfSqlNode#apply@Overridepublic boolean apply(DynamicContext context) {if (evaluator.evaluateBoolean(test, context.getBindings())) {// 如果为true,追加SQL片段contents.apply(context);return true;}return false;}
# org.apache.ibatis.scripting.xmltags.OgnlCache#getValuepublic static Object getValue(String expression, Object root) {try {Map context = Ognl.createDefaultContext(root, MEMBER_ACCESS, CLASS_RESOLVER, null);// 获取OGNL表达式的值return Ognl.getValue(parseExpression(expression), context, root);} catch (OgnlException e) {throw new BuilderException("Error evaluating expression '" + expression + "'. Cause: " + e, e);}}private static Object parseExpression(String expression) throws OgnlException {Object node = expressionCache.get(expression);if (node == null) {node = Ognl.parseExpression(expression);expressionCache.put(expression, node);}return node;}

在这个方法中,Ognl.getValue 会评估 test 条件的结果。如果 test 返回 true,那么相应的 SQL 片段就会被拼接到最终的查询中。
在这里插入图片描述
在这里插入图片描述

b. OGNL 类型转换:OgnlRuntime.convertValue

OGNL 在执行表达式时会对输入的值进行类型转换,尤其是在数字与字符串比较时。如果传入的是一个空字符串,OGNL 会将其隐式地转换为数字 0,导致条件判断被误判为 true

  • 源码路径ognl.OgnlRuntime
  • 关键方法convertValue(Object value, Class targetType)
public static Object convertValue(Object value, Class targetType) {if (targetType == int.class || targetType == Integer.class) {if (value instanceof String) {return Integer.valueOf((String) value);  // 将空字符串转换为 0}}return value;
}

在这段代码中,如果 value 是一个字符串,OGNL 会尝试将其转换为 Integer。空字符串会被转换为 0,导致条件 isCollect == 0 被误评估为 true

3. 解决方案

为了避免空字符串被错误地转换为 0,在 test 条件中显式检查 isCollect 是否为 null 或空字符串。

a. 显式检查 null 和空字符串

通过添加显式的判断条件,可以确保 isCollect 既不为 null 也不为空字符串,从而避免 test="isCollect == 0" 误判。改写后的 SQL 语句如下:

<if test="isCollect != null and isCollect != ''"><choose><when test="isCollect == 1">AND exists(select 1 from file_table imgfile2 where task.IMAGE_SEQ=imgfile2.IMAGE_SEQ and imgfile2.DOWNLOAD_STATUS = 1)</when><when test="isCollect == 0">AND not exists(select 1 from file_table imgfile2 where task.IMAGE_SEQ=imgfile2.IMAGE_SEQ and imgfile2.DOWNLOAD_STATUS = 1)</when></choose>
</if>

通过这种方式,我们可以确保只有在 isCollect 不为 null 且不为空字符串时,才会进行 test="isCollect == 0" 判断,避免空字符串误判为 0

4. 总结

  • OGNL 表达式:在 MyBatis 中,test="isCollect == 0" 会使用 OGNL 解析,空字符串会被隐式转换为 0,导致条件判断错误。
  • 类型转换:OGNL 会自动将空字符串 "" 转换为数字 0,从而导致 test="isCollect == 0" 被误判为 true
  • 解决方案:通过显式检查 isCollect != null && isCollect != '' 来避免空字符串被误判为 0

参考资料

  • MyBatis 官方文档:MyBatis

相关文章:

解决 MyBatis 中空字符串与数字比较引发的条件判断错误

问题复现 假设你在 MyBatis 的 XML 配置中使用了如下代码&#xff1a; <if test"isCollect ! null"><choose><when test"isCollect 1">AND exists(select 1 from file_table imgfile2 where task.IMAGE_SEQimgfile2.IMAGE_SEQ and im…...

python 词向量的代码解读 self.word_embeds = nn.Embedding(vocab_size, embedding_dim) 解释下

在PyTorch中&#xff0c;nn.Embedding 是一个用于将稀疏的离散数据表示为密集的嵌入向量的模块。这在自然语言处理&#xff08;NLP&#xff09;任务中非常常见&#xff0c;例如在处理单词或字符时&#xff0c;我们通常需要将这些离散的标识符转换为可以被神经网络处理的连续值向…...

记一次:使用C#创建一个串口工具

前言&#xff1a;公司的上位机打不开串口&#xff0c;发送的时候设备总是关机&#xff0c;因为和这个同事关系比较好&#xff0c;编写这款软件是用C#编写的&#xff0c;于是乎帮着解决了一下&#xff08;是真解决了&#xff09;&#xff0c;然后整理了一下自己的笔记 一、开发…...

Android Studio新版本的一个资源id无法找到的bug解决

Android Studio新版本的一个资源id无法找到的bug解决 文章目录 Android Studio新版本的一个资源id无法找到的bug解决一、前言二、Android Studio的无法获取到资源id的bug1、一段简单的Java代码1、错误现象2、错误解决方法 三、其他1、小结2、gradle.properties文件 其他相关属性…...

Datawhale AI冬令营(第一期)--零基础定制你的专属大模型

本文主要简述如何快速完成和一些小细节 第一步下载嬛嬛数据集 数据来源&#xff1a;self-llm/dataset/huanhuan.json at master datawhalechina/self-llm GitHub 注意:1.一定是数据集下载完成一定是.json结尾的 2.这个是github的网址&#xff0c;可能会遇到打不开的情况 …...

LLMs之APE:基于Claude的Prompt Improver的简介、使用方法、案例应用之详细攻略

LLMs之APE&#xff1a;基于Claude的Prompt Improver的简介、使用方法、案例应用之详细攻略 目录 Prompt Improver的简介 0、背景痛点 1、优势 2、实现思路 Prompt优化 示例管理 提示词评估 Prompt Improver的使用方法 1、使用方法 Prompt Improver的案例应用 1、Kap…...

【Unity人形布娃娃插件】Ragdoll Animator

Ragdoll Animator 是一款为 Unity 引擎开发的插件&#xff0c;专注于让角色在运行时动态地切换到布娃娃物理系统&#xff08;Ragdoll Physics&#xff09;。该插件帮助开发者轻松创建逼真的角色动画过渡效果&#xff0c;尤其适用于需要角色碰撞、摔倒、受击或其他物理反应的场景…...

跨团队协作中目标一致性至关重要

在团队协作的复杂拼图里&#xff0c;目标一致性是那根贯穿始终的主线&#xff0c;缺之则拼图难成&#xff0c;团队亦难达预期之效。 且看这样一个实例&#xff1a;部门承接了业务方一项紧急的数据处理需求&#xff0c;此任务犹如一座亟待攀登的险峰&#xff0c;落在了 A 团队…...

Excel的文件导入遇到大文件时

Excel的文件导入向导如何把已导入数据排除 入起始行&#xff0c;选择从哪一行开始导入。 比如&#xff0c;前两行已经导入了&#xff0c;第二次导入的时候排除前两行&#xff0c;从第三行开始&#xff0c;就将导入起始行设置为3即可&#xff0c;且不勾选含标题行。 但遇到大文…...

使用字典进行动态编程

在你的程序中&#xff0c;你想要执行各种计算&#xff0c;例如计算卫星的总数。 此外&#xff0c;当你进行更高级的编程时&#xff0c;你可能会发现你需要从文件或数据库中加载此类信息&#xff0c;而不是直接编码到 Python 中。 为了帮助支持这些场景&#xff0c;Python 使你…...

机器学习02-发展历史补充

机器学习02-发展历史补充 文章目录 机器学习02-发展历史补充1-机器学习个人理解1-初始阶段&#xff1a;统计学习和模式识别&#xff08;20世纪50年代至80年代&#xff09;2-第二阶段【集成时代】【核方法】&#xff08;20世纪90年代至2000年代初期&#xff09;3-第三阶段【特征…...

全国青少年信息学奥林匹克竞赛(信奥赛)备考实战之计数器与累加器(一)

学习背景&#xff1a; 在现实生活中一些需要计数的场景下我们会用到计数器&#xff0c;如空姐手里记录乘客的计数器&#xff0c;跳绳手柄上的计数器等。累加器是累加器求和&#xff0c;以得到最后的结果。计数器和累加器它们虽然是基础知识&#xff0c;但是应用广泛&#xff0…...

Android的SurfaceView和TextureView介绍

文章目录 前言一、什么是SurfaceView &#xff1f;1.1 SurfaceView 使用示例1.2 SurfaceView 源码概述1.3 SurfaceView 的构造与初始化1.4 SurfaceHolder.Callback 回调接口1.5 SurfaceView 渲染机制 二、什么是TextureView&#xff1f;2.1 TextureView 使用示例2.2 TextureVie…...

Scala的集合

1 集合简介 1&#xff09;Scala 的集合有三大类&#xff1a;序列 Seq、集 Set、映射 Map&#xff0c;所有的集合都扩展自 Iterable 特质。 2&#xff09;对于几乎所有的集合类&#xff0c;Scala 都同时提供了可变和不可变的版本&#xff0c;分别位于以下两 个包 不可变集合&am…...

1. Flink自定义Source

一. Source 简介 DataStream是Flink的低级API&#xff0c;用于进行数据的实时处理&#xff0c;Flink编程模型分为Source、Transformation、Sink三个部分&#xff0c;如下图所示。 默认Flink提供了大量的内置Source&#xff0c;常见的Source如下&#xff1a; 基于文件的Sour…...

关于LinuxWindows双系统在八月更新后出现的问题

问题描述类似于&#xff1a;Verifying shim SBAT data failed: If you are, this is caused by a reported problem in the August update if you can get into Windows, either uninstall the August update, or open Command Prompt as administrator and run this command,…...

VMware:如何在CentOS7上开启22端口

打开虚拟机&#xff1a;【编辑】【虚拟机网络设置】 其中填入的虚拟机IP地址是虚拟机中centos的IP地址&#xff0c;虚拟机端口为需要映射的centos端口 配置好之后保存&#xff0c;打开宿主机 win cmd telnet 192.168.1.26 22 如果出现上述窗口&#xff0c;则说明已经成功开放…...

ubuntu远程桌面开启opengl渲染权限

背景 最近用windows的【远程桌面连接】登录ubuntu后&#xff08;xrdp协议&#xff09;&#xff0c;发现gl环境是集显的&#xff0c;但是本地登录ubuntu桌面后是独显&#xff08;英伟达&#xff09;&#xff0c;想要在远程桌面上也用独显渲染环境。 一、查看是独显还是集显环境…...

从小学题到技术选型哲学:以智能客服系统为例,解读相关AI技术栈20241211

&#x1f9e0;&#x1f4a1;从小学题到技术选型哲学&#xff1a;以智能客服系统为例&#xff0c;解读相关AI技术栈 引言&#xff1a;从小学数学题到技术智慧 &#x1f4da;✨ 在小学数学题中&#xff0c;有这样一道问题&#xff1a; “一个长方形变成平行四边形后&#xff0c…...

【C语言练习(5)—回文数判断】

C语言练习&#xff08;5&#xff09; 文章目录 C语言练习&#xff08;5&#xff09;前言问题问题解析结果总结 前言 通过回文数练习&#xff0c;巩固数字取余和取商如何写代码 问题 输入一个五位数判断是否为回文数&#xff1f; 问题解析 回文数是指正读反读都一样的整数。…...

零门槛NAS搭建:WinNAS如何让普通电脑秒变私有云?

一、核心优势&#xff1a;专为Windows用户设计的极简NAS WinNAS由深圳耘想存储科技开发&#xff0c;是一款收费低廉但功能全面的Windows NAS工具&#xff0c;主打“无学习成本部署” 。与其他NAS软件相比&#xff0c;其优势在于&#xff1a; 无需硬件改造&#xff1a;将任意W…...

Java 语言特性(面试系列1)

一、面向对象编程 1. 封装&#xff08;Encapsulation&#xff09; 定义&#xff1a;将数据&#xff08;属性&#xff09;和操作数据的方法绑定在一起&#xff0c;通过访问控制符&#xff08;private、protected、public&#xff09;隐藏内部实现细节。示例&#xff1a; public …...

SCAU期末笔记 - 数据分析与数据挖掘题库解析

这门怎么题库答案不全啊日 来简单学一下子来 一、选择题&#xff08;可多选&#xff09; 将原始数据进行集成、变换、维度规约、数值规约是在以下哪个步骤的任务?(C) A. 频繁模式挖掘 B.分类和预测 C.数据预处理 D.数据流挖掘 A. 频繁模式挖掘&#xff1a;专注于发现数据中…...

YSYX学习记录(八)

C语言&#xff0c;练习0&#xff1a; 先创建一个文件夹&#xff0c;我用的是物理机&#xff1a; 安装build-essential 练习1&#xff1a; 我注释掉了 #include <stdio.h> 出现下面错误 在你的文本编辑器中打开ex1文件&#xff0c;随机修改或删除一部分&#xff0c;之后…...

2023赣州旅游投资集团

单选题 1.“不登高山&#xff0c;不知天之高也&#xff1b;不临深溪&#xff0c;不知地之厚也。”这句话说明_____。 A、人的意识具有创造性 B、人的认识是独立于实践之外的 C、实践在认识过程中具有决定作用 D、人的一切知识都是从直接经验中获得的 参考答案: C 本题解…...

Vue3 PC端 UI组件库我更推荐Naive UI

一、Vue3生态现状与UI库选择的重要性 随着Vue3的稳定发布和Composition API的广泛采用&#xff0c;前端开发者面临着UI组件库的重新选择。一个好的UI库不仅能提升开发效率&#xff0c;还能确保项目的长期可维护性。本文将对比三大主流Vue3 UI库&#xff08;Naive UI、Element …...

Python爬虫实战:研究Restkit库相关技术

1. 引言 1.1 研究背景与意义 在当今信息爆炸的时代,互联网上存在着海量的有价值数据。如何高效地采集这些数据并将其应用于实际业务中,成为了许多企业和开发者关注的焦点。网络爬虫技术作为一种自动化的数据采集工具,可以帮助我们从网页中提取所需的信息。而 RESTful API …...

Django RBAC项目后端实战 - 03 DRF权限控制实现

项目背景 在上一篇文章中&#xff0c;我们完成了JWT认证系统的集成。本篇文章将实现基于Redis的RBAC权限控制系统&#xff0c;为系统提供细粒度的权限控制。 开发目标 实现基于Redis的权限缓存机制开发DRF权限控制类实现权限管理API配置权限白名单 前置配置 在开始开发权限…...

RushDB开源程序 是现代应用程序和 AI 的即时数据库。建立在 Neo4j 之上

一、软件介绍 文末提供程序和源码下载 RushDB 改变了您处理图形数据的方式 — 不需要 Schema&#xff0c;不需要复杂的查询&#xff0c;只需推送数据即可。 二、Key Features ✨ 主要特点 Instant Setup: Be productive in seconds, not days 即时设置 &#xff1a;在几秒钟…...

ubuntu清理垃圾

windows和ubuntu 双系统&#xff0c;ubuntu 150GB&#xff0c;开发用&#xff0c;基本不装太多软件。但是磁盘基本用完。 1、查看home目录 sudo du -h -d 1 $HOME | grep -v K 上面的命令查看$HOME一级目录大小&#xff0c;发现 .cache 有26GB&#xff0c;.local 有几个GB&am…...