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:数列求值 题目描述 本题为填空题,只需要算出结果后,在代码中使用输出语句将所填结果输出…...
AI Agent与Agentic AI:原理、应用、挑战与未来展望
文章目录 一、引言二、AI Agent与Agentic AI的兴起2.1 技术契机与生态成熟2.2 Agent的定义与特征2.3 Agent的发展历程 三、AI Agent的核心技术栈解密3.1 感知模块代码示例:使用Python和OpenCV进行图像识别 3.2 认知与决策模块代码示例:使用OpenAI GPT-3进…...
Qt Widget类解析与代码注释
#include "widget.h" #include "ui_widget.h"Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget) {ui->setupUi(this); }Widget::~Widget() {delete ui; }//解释这串代码,写上注释 当然可以!这段代码是 Qt …...
【快手拥抱开源】通过快手团队开源的 KwaiCoder-AutoThink-preview 解锁大语言模型的潜力
引言: 在人工智能快速发展的浪潮中,快手Kwaipilot团队推出的 KwaiCoder-AutoThink-preview 具有里程碑意义——这是首个公开的AutoThink大语言模型(LLM)。该模型代表着该领域的重大突破,通过独特方式融合思考与非思考…...
【项目实战】通过多模态+LangGraph实现PPT生成助手
PPT自动生成系统 基于LangGraph的PPT自动生成系统,可以将Markdown文档自动转换为PPT演示文稿。 功能特点 Markdown解析:自动解析Markdown文档结构PPT模板分析:分析PPT模板的布局和风格智能布局决策:匹配内容与合适的PPT布局自动…...
生成 Git SSH 证书
🔑 1. 生成 SSH 密钥对 在终端(Windows 使用 Git Bash,Mac/Linux 使用 Terminal)执行命令: ssh-keygen -t rsa -b 4096 -C "your_emailexample.com" 参数说明: -t rsa&#x…...
Python爬虫(一):爬虫伪装
一、网站防爬机制概述 在当今互联网环境中,具有一定规模或盈利性质的网站几乎都实施了各种防爬措施。这些措施主要分为两大类: 身份验证机制:直接将未经授权的爬虫阻挡在外反爬技术体系:通过各种技术手段增加爬虫获取数据的难度…...
linux 下常用变更-8
1、删除普通用户 查询用户初始UID和GIDls -l /home/ ###家目录中查看UID cat /etc/group ###此文件查看GID删除用户1.编辑文件 /etc/passwd 找到对应的行,YW343:x:0:0::/home/YW343:/bin/bash 2.将标红的位置修改为用户对应初始UID和GID: YW3…...
Android15默认授权浮窗权限
我们经常有那种需求,客户需要定制的apk集成在ROM中,并且默认授予其【显示在其他应用的上层】权限,也就是我们常说的浮窗权限,那么我们就可以通过以下方法在wms、ams等系统服务的systemReady()方法中调用即可实现预置应用默认授权浮…...
Caliper 配置文件解析:config.yaml
Caliper 是一个区块链性能基准测试工具,用于评估不同区块链平台的性能。下面我将详细解释你提供的 fisco-bcos.json 文件结构,并说明它与 config.yaml 文件的关系。 fisco-bcos.json 文件解析 这个文件是针对 FISCO-BCOS 区块链网络的 Caliper 配置文件,主要包含以下几个部…...
HashMap中的put方法执行流程(流程图)
1 put操作整体流程 HashMap 的 put 操作是其最核心的功能之一。在 JDK 1.8 及以后版本中,其主要逻辑封装在 putVal 这个内部方法中。整个过程大致如下: 初始判断与哈希计算: 首先,putVal 方法会检查当前的 table(也就…...
