Mybatis动态sql语句foreach中拼接正则表达式字符串注意事项
今天要说到的查询情况,平时项目里边其实用到的并不是很多,使用正则表达式无非是为了匹配结果比较灵活,最常见的,我们的查询条件一般一个参数仅仅只是一种情况的筛选,对于如何选择查询方式,主要还是要看前端传递的查询条件格式,比如单参数只表示单个值,而且数据表对应也是单个值,这种最简单,精确匹配的话,就直接where 字段=参数值,如果模糊查询,就使用like匹配,如果前端传递的某个参数,值是用逗号分割的一个字符串,实际上可以看做是多个匹配条件,而数据表中,对应的字段值是单个值的话,可以where find_in_set(字段,字符串参数值)或者将分割的字符串转换成List,然后用in匹配
还有一种就是前端传递的参数值是逗号分割的字符串,对应要查询匹配的字段,数据表中存的也是以逗号分割的字符串,查询的时候,一般是或关系,就需要从表中查询出只要包含查询条件中的其中一个值的匹配结果
举例来说吧,比如昨天一个需求是需要根据标签id来查询只要含有这些标签中其中给一个标签的企业。
先看swagger文档:
这是一个get请求方式的分页查询,重点就是下边那个tagIdList参数,前端传递的是 一个逗号分割的id字符串,是两个标签,而企业也是多标签的,现在从企业中查询出有这两个标签的企业(其实就是或的关系)
接下来看mybatis xml中的sql写法吧
<select id="pageList" resultType="com.xiaomifeng1010.response.vo.EtpSrvcListedCltvtnVO">selectcltvtn.id,SUBSTRING_INDEX(cltvtn.state_record,',',-1) as state,cltvtn.name,(select name from sys_dict where code = cltvtn.nature) as nature,(select name from sys_dict where code = cltvtn.register_region) as register_region,(select name from sys_dict where code = cltvtn.industry) as industry,cltvtn.uscc,cltvtn.master_data_id,cltvtn.wr_tech_type_mid_small_etp,cltvtn.wr_high_new_tech_industry,group_concat(distinct tag.tag_name) as tag_namesfrometp_srvc_listed_cltvtn cltvtnjoin enterprise_info infoon cltvtn.uscc=info.usccleft join enterprise_tag_mid mid on info.id=mid.enterprise_idleft join enterprise_tag tag on mid.tag_id=tag.idwhere cltvtn.del_flag = 0<if test="param.name != null and param.name != ''">and cltvtn.name like concat('%',#{param.name},'%')</if><if test="param.uscc != null and param.uscc != ''">and cltvtn.uscc like concat('%',#{param.uscc},'%')</if><if test="param.nature != null">and cltvtn.nature = #{param.nature}</if><if test="param.registerRegion != null and param.registerRegion != ''">and cltvtn.register_region = #{param.registerRegion}</if><if test="param.state!=null">and SUBSTRING_INDEX(cltvtn.state_record,',',-1)=#{param.state,jdbcType=INTEGER}</if>group by cltvtn.name,cltvtn.uscc<if test="param.tagIdList != null and param.tagIdList.size() != 0">HAVING group_concat(distinct tag.id) REGEXPreplace(<foreach collection="param.tagIdList" item="tagId" separator="|" open="concat("close=")">#{tagId,jdbcType=BIGINT}</foreach>,' ','')</if><if test="param.sort != '' and param.sort != null">order by ${param.sort}</if><if test="param.sort == '' or param.sort == null">order by cltvtn.create_time desc</if></select>
要匹配标签或关系:
我一开始是这样写的,为什么这样写的,因为以前我使用Integer类型的枚举值List时候这样用是可以正常匹配的,所以Id值是Long类型的依然这样用,但是结果查询结果出乎我的预料,当我选择单个标签或者不选标签的时候,是可以正常查询结果的,但是当我选择的是两个标签时候,奇怪的事情就发生了,居然一条结果都没有了!像下边这样:
我选单个标签的时候,是可以查询出231条结果的
但是当我选择两个标签的时候,居然一条结果都没有:
正常情况下,foreach拼接的tagId,拼接好,完整的sql应该是这样的:
我在navicat中直接执行这个语句是可以正常查询到结果的,但是在程序中就是没有查询结果,所以我又去试了一下测试环境,并查看日志,并且我把#{tagId}换成了${tagId}方便直接打印sql的时候直接带上参数,而不是占位符,看到的日志信息就是这样的:
可以看到使用concat拼接的正则表达式每个id以及‘|’符号之间都加上了空格,应该是mybatis加上的,所以为了去掉空格,所以我又加上了replace函数,然后在本地测试,结果本地可以查询出来一些结果了,但是结果却是只有匹配第二个标签的结果,好家伙,replace没有替换掉‘|’符号前边那个id的空格?然后发到测试环境,结果测试环境,还是查不到结果,就非常诡异,正常情况下replace是可以替换掉字符串中所有空格的,在Navicat中运行也是正常的:
既然mybatis会改变最终的拼接结果,那么索性不用mybatis来拼接了,解决方案是使用ognl表达式,直接用java的api就可以了,所以最终改写成了:
<select id="pageList" resultType="com.xiaomifeng1010.response.vo.EtpSrvcListedCltvtnVO">selectcltvtn.id,SUBSTRING_INDEX(cltvtn.state_record,',',-1) as state,cltvtn.name,(select name from sys_dict where code = cltvtn.nature) as nature,(select name from sys_dict where code = cltvtn.register_region) as register_region,(select name from sys_dict where code = cltvtn.industry) as industry,cltvtn.uscc,cltvtn.master_data_id,cltvtn.wr_tech_type_mid_small_etp,cltvtn.wr_high_new_tech_industry,group_concat(distinct tag.tag_name) as tag_namesfrometp_srvc_listed_cltvtn cltvtnjoin enterprise_info infoon cltvtn.uscc=info.usccleft join enterprise_tag_mid mid on info.id=mid.enterprise_idleft join enterprise_tag tag on mid.tag_id=tag.idwhere cltvtn.del_flag = 0<if test="param.name != null and param.name != ''">and cltvtn.name like concat('%',#{param.name},'%')</if><if test="param.uscc != null and param.uscc != ''">and cltvtn.uscc like concat('%',#{param.uscc},'%')</if><if test="param.nature != null">and cltvtn.nature = #{param.nature}</if><if test="param.registerRegion != null and param.registerRegion != ''">and cltvtn.register_region = #{param.registerRegion}</if><if test="param.state!=null">and SUBSTRING_INDEX(cltvtn.state_record,',',-1)=#{param.state,jdbcType=INTEGER}</if>group by cltvtn.name,cltvtn.uscc<if test="param.tagIdList != null and param.tagIdList.size() != 0"><bind name="joiner" value="@com.google.common.base.Joiner@on('|')"/><bind name="tagIds" value="joiner.join(param.tagIdList)"/>HAVING group_concat(distinct tag.id) REGEXP#{tagIds}</if><if test="param.sort != '' and param.sort != null">order by ${param.sort}</if><if test="param.sort == '' or param.sort == null">order by cltvtn.create_time desc</if></select>
然后再到swagger文档中测试,就可以正常查询到准确的结果条数了:
但是如果是Integer类型的枚举值列表,使用foreach来拼接正则表达式,目前测试是可以正常查询的,比如另外一个查询场景:之前的一个业务,在新增金融政策的时候, 政策类型下拉选只能单选,所以设计的数据表字段直接是枚举int类型,1,2,3来存储的,后来要求这个字段可以下拉多选,所以就改成了字符串类型,多选的时候,传递过来的参数值就类似‘1,2,3’这样逗号拼接的字符串存储到该字段中,同时查询也由单选改成了多选:
所以查询也要改写成这样:
<select id="pageList" resultType="com.xiaomifeng1010.response.vo.FinancePolicyVO">selectfp.id,fp.title,fp.publish_date,fp.content,fp.publish_office,fp.written_date,fp.typefrom finance_policy fpwhere fp.del_flag = 0<if test="param.title != '' and param.title != null">and fp.title like concat('%',#{param.title},'%')</if><if test="param.supportType != '' and param.supportType != null">and fp.support_type like concat('%',#{param.supportType},'%')</if><if test="param.supportMode != '' and param.supportMode != null">and fp.support_mode like concat('%',#{param.supportMode},'%')</if><if test="@org.apache.commons.lang3.StringUtils@isNotBlank(param.type)">and fp.type like concat('%',#{param.type},'%')</if><if test="@org.apache.commons.collections4.CollectionUtils@isNotEmpty(param.typeList)">and fp.type REGEXP<foreach collection="param.typeList" open="concat(" item="type" close=")" separator="|">#{type,jdbcType=INTEGER}</foreach></if><if test="param.dateRange != null">and fp.publish_date >= subdate(CURRENT_DATE,#{param.dateRange})</if><if test="param.sort != '' and param.sort != null">order by ${param.sort}</if><if test="param.sort == '' or param.sort == null">order by fp.publish_date desc</if></select>
注意这里拼接的是integer类型的值:
没有出现查询不到结果的情况,当前端选择两种政策类型时,只要包含这两种类型的一种都会查询出来:
可以看到type是2或者3的都可以查出来,以及同时是2,3的也可以查出来
最后再说明一下其他注意事项:
如果要在foreach中使用#{item}的形式,那么open和close的值不能直接是单引号,因为#参数值两边不能直接是单引号,open值可以是concat(,close的值是)这样的,如果要用单引号也不是不可以,将#换成$就可以了
另外还有一种就是匹配英文字符串或者中文的那种情况,比如查询某字段包含某个编码或另一个编码,以及某字段是否包含某段文字或其他文字之类的,也可以使用全文索引,需要对查询字段创建全文索引,并且查询语句使用match against形式查询语句,具体的匹配模式可以查询相关资料
不过全文索引创建的字段,只对新添加或更新的行数据生效,也就是说即使旧数据满足匹配条件也查询不出来,所以可以做全表更新或者旧数据清除处理之后,再用全文索引匹配
相关文章:

Mybatis动态sql语句foreach中拼接正则表达式字符串注意事项
今天要说到的查询情况,平时项目里边其实用到的并不是很多,使用正则表达式无非是为了匹配结果比较灵活,最常见的,我们的查询条件一般一个参数仅仅只是一种情况的筛选,对于如何选择查询方式,主要还是要看前端…...

JVM内置锁synchronized关键字详解
目录 JVM内置锁synchronized关键字详解 设计同步器的意义 如何解决线程并发安全问题? synchronized原理详解 synchronized底层原理 synchronized在jdk1.6前后的变化【重点】 jdk小于1.6时 jdk>1.6时 轻量级锁何时升级为重量级锁??…...

【2021.12.25】xv6系统入门学习
【2021.12.28】为xv6系统添加一个开机密码 文章目录【2021.12.28】为xv6系统添加一个开机密码0、说明1、Ubuntu20上安装xv62、测试指令3、修改系统代码4、添加自己的程序命令0、说明 xv6 是 MIT 设计的一个教学型操纵系统。 记录Ubuntu上安装x86版本的xv6系统,为其…...
Linux内核4.14版本——drm框架分析(4)——crtc分析
目录 1. struct drm_crtc结构体 2. crtc相关的API 2.1 drm_crtc_init_with_planes 2.5 drm_mode_setcrtc 3. func的一些介绍 3.1 struct drm_crtc_helper_funcs 3.2 struct drm_crtc_funcs 4. 应用层的调用 4.1 drmModeSetCrtc (drmlib库里)---…...

用原生js手写分页功能
分页功能如下: 数据分页显示,每页显示若干条数据,默认当前页码为第一页。例如:每页5条数据,则第一页显示 1-5 条,第二页显示 6-10 条,依此类推。当页码为第一页时,上一页为禁用状态…...

CornerNet介绍
CornerNet: Detecting Objects as Paired Keypoints ECCV 2018 Paper:https://arxiv.org/pdf/1808.01244v2.pdf Code:GitHub - princeton-vl/CornerNet 摘要: 提出了一种single-stage的目标检测算法CornerNet,它把每个目标检…...
【SpringBoot】日志使用
默认配置 Spring Boot默认帮我们配置好了日志 //记录器Logger logger LoggerFactory.getLogger(getClass());Testpublic void contextLoads() {//System.out.println();//日志的级别;//由低到高 trace<debug<info<warn<error//可以调整输出的日志级…...
关于slice扩容性能损耗的探究
背景 如果让我评选最伟大的数据结构,在我心中答案只有两个,数组和哈希表,这两个是我的程序的重要组成部分,同时也是我饭碗的重要组成部分。slice和map简洁明了的API很容易让我们有一种他们提供了无限大的空间,可以…...

Java实现单向链表
✅作者简介:热爱Java后端开发的一名学习者,大家可以跟我一起讨论各种问题喔。 🍎个人主页:Hhzzy99 🍊个人信条:坚持就是胜利! 💞当前专栏:Java数据结构与算法 ǹ…...
3月4日,30秒知全网,精选7个热点
///印度最大供电商罕见于现货市场购煤,能源供应短缺成忧 据知情人士透露,这家印度国有发电公司计划在下周左右发布300万吨的招标 ///QQ音乐推出AIGC黑胶播放器 这是国内音乐行业首个运用AI技术,通过文字、图片指令快速生成不同风格的播放器…...

EXCEL-职业版本(2)
Excel-职业版本(2) 定位 1.如何快速定位到不连续的空值,填充为0 1.在任意空单元格里复制0 2.选中数据区域CtrlA 3.CtrlG 4.选择【定位条件】 5.选择【空值】 6.ctrlV 粘贴 即可 2.怎么一次性计算每个小组的数量 单价和金额的和? 1.选中…...
java中延时队列的实现
大家好,我是一名CRUD工程师,最近我朋友突然来问我如何实现延时队列,我脱口而出就是MQ。不过突然想到公司的项目好像用的是java的一个原生类。于是我就想着趁周末的时间好好的去探究一下各方法实现延时队列的优缺点。 延迟消息 延迟消息就是字…...
基于java的circle buffer的实现
总目录链接==>> AutoSAR入门和实战系列总目录 文章目录 缓冲区示例什么是循环缓冲区?方法 1:使用数组插入元素删除元素方法 2:使用链表插入元素:删除元素:当数据经常从一个地方移动到另一个地方或从一个进程移动到另一个进程或被频繁访问时,它不能存储在永久性内存…...
通用方法——为什么重写equals还要重写hashcode
本文介绍java.lang.Object类中的两个方法:equals和hashCode。这两个方法大家应该都知道,但是这两个方法的作用是什么、为什么重写equals还要重写hashCode、它们之间有什么关系和约定等,今天就来带大家了解一下。 1、hashCode hashCode即散列…...

JavaSE学习进阶day2_01 包和权限修饰符
第一章 包 1.1 包 包在操作系统中其实就是一个文件夹。包是用来分门别类的管理技术,不同的技术类放在不同的包下,方便管理和维护。 在IDEA项目中,建包的操作如下: 这个咱们在基础班就谈到过。 包名的命名规范: 路径…...
Android性能调优 - 省电优化
省电:通过工具Battery Historian查看到:耗电大头: 屏幕、网络、cpuled/oled屏幕显示:降低亮度,开深色模式;锁屏间隔缩短到 ;亮屏需要一直持有唤醒锁,还有gps定位也需要用到唤醒锁;网络: 常用的网络优化措施…...
ElasticSearch - SpringBoot整合ES之全文搜索匹配查询 match
文章目录1. 数据准备2. match 匹配查询1. 全文检索2. 简化查询DSL语句3. match 匹配查询原理官方文档地址:https://www.elastic.co/guide/en/elasticsearch/reference/index.html权威指南:https://www.elastic.co/guide/cn/elasticsearch/guide/current/…...

句子的改写和扩写
目录 1.句子改写 2.句子扩写 (不低于15个句子算是长句子,不能太多长句子) 1.句子改写 我绝不会嫁给你的。 如果你是世界上最后一个男人,我就去寺庙。 If you married me,I would jump into the well. 如果你嫁给我,我…...

DockerFile创建及案例
DockerFile dockerfile是用来构建docker镜像的文件,命令脚本参数脚本! 构建步骤 编写一个dockerfile文件docker build 构建成为一个对象docker run 运行镜像docker push 发布镜像(DockerHub、阿里云镜像仓库) 去官网Docker-Hub…...

第十四届蓝桥杯三月真题刷题训练——第 1 天
目录 题目1:数列求值 代码: 题目2:质数 代码: 题目3:饮料换购 代码: 题目1:数列求值 题目描述 本题为填空题,只需要算出结果后,在代码中使用输出语句将所填结果输出…...
ES6从入门到精通:前言
ES6简介 ES6(ECMAScript 2015)是JavaScript语言的重大更新,引入了许多新特性,包括语法糖、新数据类型、模块化支持等,显著提升了开发效率和代码可维护性。 核心知识点概览 变量声明 let 和 const 取代 var…...
模型参数、模型存储精度、参数与显存
模型参数量衡量单位 M:百万(Million) B:十亿(Billion) 1 B 1000 M 1B 1000M 1B1000M 参数存储精度 模型参数是固定的,但是一个参数所表示多少字节不一定,需要看这个参数以什么…...

【HarmonyOS 5.0】DevEco Testing:鸿蒙应用质量保障的终极武器
——全方位测试解决方案与代码实战 一、工具定位与核心能力 DevEco Testing是HarmonyOS官方推出的一体化测试平台,覆盖应用全生命周期测试需求,主要提供五大核心能力: 测试类型检测目标关键指标功能体验基…...

【Zephyr 系列 10】实战项目:打造一个蓝牙传感器终端 + 网关系统(完整架构与全栈实现)
🧠关键词:Zephyr、BLE、终端、网关、广播、连接、传感器、数据采集、低功耗、系统集成 📌目标读者:希望基于 Zephyr 构建 BLE 系统架构、实现终端与网关协作、具备产品交付能力的开发者 📊篇幅字数:约 5200 字 ✨ 项目总览 在物联网实际项目中,**“终端 + 网关”**是…...
根据万维钢·精英日课6的内容,使用AI(2025)可以参考以下方法:
根据万维钢精英日课6的内容,使用AI(2025)可以参考以下方法: 四个洞见 模型已经比人聪明:以ChatGPT o3为代表的AI非常强大,能运用高级理论解释道理、引用最新学术论文,生成对顶尖科学家都有用的…...
鸿蒙DevEco Studio HarmonyOS 5跑酷小游戏实现指南
1. 项目概述 本跑酷小游戏基于鸿蒙HarmonyOS 5开发,使用DevEco Studio作为开发工具,采用Java语言实现,包含角色控制、障碍物生成和分数计算系统。 2. 项目结构 /src/main/java/com/example/runner/├── MainAbilitySlice.java // 主界…...

JVM 内存结构 详解
内存结构 运行时数据区: Java虚拟机在运行Java程序过程中管理的内存区域。 程序计数器: 线程私有,程序控制流的指示器,分支、循环、跳转、异常处理、线程恢复等基础功能都依赖这个计数器完成。 每个线程都有一个程序计数…...
SQL慢可能是触发了ring buffer
简介 最近在进行 postgresql 性能排查的时候,发现 PG 在某一个时间并行执行的 SQL 变得特别慢。最后通过监控监观察到并行发起得时间 buffers_alloc 就急速上升,且低水位伴随在整个慢 SQL,一直是 buferIO 的等待事件,此时也没有其他会话的争抢。SQL 虽然不是高效 SQL ,但…...

Selenium常用函数介绍
目录 一,元素定位 1.1 cssSeector 1.2 xpath 二,操作测试对象 三,窗口 3.1 案例 3.2 窗口切换 3.3 窗口大小 3.4 屏幕截图 3.5 关闭窗口 四,弹窗 五,等待 六,导航 七,文件上传 …...

FFmpeg:Windows系统小白安装及其使用
一、安装 1.访问官网 Download FFmpeg 2.点击版本目录 3.选择版本点击安装 注意这里选择的是【release buids】,注意左上角标题 例如我安装在目录 F:\FFmpeg 4.解压 5.添加环境变量 把你解压后的bin目录(即exe所在文件夹)加入系统变量…...