mybatis多条件in查询拓展
背景
最近碰上有个业务,查询的sql如下:
select * from table where (sku_id,batch_no) in ((#{skuId},#{batchNo}),...);
本来也没什么,很简单常见的一种sql。问题是我们使用的是mybatis-plus,然后写的时候又没有考虑到后面的查询条件,这里用的是mybatis-plus lambda的方式。
LambdaQueryChainWrapper<Table> query = tableService.lambdaQuery();
query.eq(Table::getId, param.getId());
但是mysql-plus并没有支持这种sql的形式,要么用apply方法自定义拼接sql,要么不采用lambda方式,将语句写成 xml 形式。
不过,第一种方式感觉很 low,一大段 java 代码里插入一段sql字符串,看上去就很别扭,因为有点代码洁癖,只能果断放弃。
第二种改动又太大,前面那么多查询条件,又要全部移入xml里面,同时,本来已经通过测试的筛选条件,又要重新来一遍,太懒,实在干不动了。
想想这么常见的场景,网上理应有现成的解决方案。但不知道是搜索关键字不对还是确实没有,搜了半天没搜出来。
最后无奈,只能尝试自己扩展一下。
代码
import com.baomidou.mybatisplus.core.conditions.AbstractLambdaWrapper;
import com.baomidou.mybatisplus.core.conditions.ISqlSegment;
import com.baomidou.mybatisplus.core.conditions.segments.MergeSegments;
import com.baomidou.mybatisplus.core.metadata.TableFieldInfo;
import com.baomidou.mybatisplus.core.toolkit.ArrayUtils;
import com.baomidou.mybatisplus.core.toolkit.StringPool;
import com.baomidou.mybatisplus.core.toolkit.TableInfoHelper;
import com.baomidou.mybatisplus.core.toolkit.support.SFunction;
import io.vavr.Tuple;
import org.springframework.util.Assert;import java.util.List;
import java.util.Map;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Predicate;
import java.util.stream.Collectors;import static com.baomidou.mybatisplus.core.enums.SqlKeyword.IN;
import static java.util.stream.Collectors.joining;public class CombinationLambdaQueryChainWrapper<T> extends AbstractLambdaWrapper<T, CombinationLambdaQueryChainWrapper<T>> {/*** 查询字段*/private String sqlSelect;public CombinationLambdaQueryChainWrapper() {this(null);}public CombinationLambdaQueryChainWrapper(T entity) {this.entity = entity;this.initEntityClass();this.initNeed();}CombinationLambdaQueryChainWrapper(T entity, Class<T> entityClass, String sqlSelect, AtomicInteger paramNameSeq, Map<String, Object> paramNameValuePairs,MergeSegments mergeSegments) {this.entity = entity;this.paramNameSeq = paramNameSeq;this.paramNameValuePairs = paramNameValuePairs;this.expression = mergeSegments;this.sqlSelect = sqlSelect;this.entityClass = entityClass;}/*** <p>* SELECT 部分 SQL 设置* </p>** @param columns 查询字段*/@SafeVarargspublic final CombinationLambdaQueryChainWrapper<T> select(SFunction<T, ?>... columns) {if (ArrayUtils.isNotEmpty(columns)) {this.sqlSelect = this.columnsToString(columns);}return typedThis;}public CombinationLambdaQueryChainWrapper<T> select(Predicate<TableFieldInfo> predicate) {return select(entityClass, predicate);}/*** <p>* 过滤查询的字段信息(主键除外!)* </p>* <p>* 例1: 只要 java 字段名以 "test" 开头的 -> select(i -> i.getProperty().startsWith("test"))* 例2: 只要 java 字段属性是 CharSequence 类型的 -> select(TableFieldInfo::isCharSequence)* 例3: 只要 java 字段没有填充策略的 -> select(i -> i.getFieldFill == FieldFill.DEFAULT)* 例4: 要全部字段 -> select(i -> true)* 例5: 只要主键字段 -> select(i -> false)* </p>** @param predicate 过滤方式* @return this*/public CombinationLambdaQueryChainWrapper<T> select(Class<T> entityClass, Predicate<TableFieldInfo> predicate) {this.entityClass = entityClass;this.sqlSelect = TableInfoHelper.getTableInfo(getCheckEntityClass()).chooseSelect(predicate);return typedThis;}@Overridepublic String getSqlSelect() {return sqlSelect;}/*** <p>* 用于生成嵌套 sql* 故 sqlSelect 不向下传递* </p>*/@Overrideprotected CombinationLambdaQueryChainWrapper<T> instance(AtomicInteger paramNameSeq, Map<String, Object> paramNameValuePairs) {return new CombinationLambdaQueryChainWrapper<>(entity, entityClass, null, paramNameSeq, paramNameValuePairs, new MergeSegments());}/*** 组合IN查询* 此处引入元祖,需使用io.vavr包**/public CombinationLambdaQueryChainWrapper<T> combinationIn(boolean condition, List<Tuple> list, SFunction<T, ?>... columns) {Assert.isTrue(columns.length == list.get(0).arity(), "请检查组合IN查询参数长度");return doIt(condition,() -> StringPool.LEFT_BRACKET + columnsToString(columns) + StringPool.RIGHT_BRACKET,IN,inExpressionOfParam(list));}private ISqlSegment inExpressionOfParam(List<Tuple> list) {if (list.size() > 1000) {throw new RuntimeException("组合查询禁止超过1000条");}List<String> result = list.stream().map(m -> StringPool.LEFT_BRACKET + m.toSeq().asJava().stream().map(n -> n instanceof String ? StringPool.SINGLE_QUOTE + n + StringPool.SINGLE_QUOTE : String.valueOf(n)).collect(Collectors.joining(StringPool.COMMA)) + StringPool.RIGHT_BRACKET).collect(Collectors.toList());return () -> result.stream().collect(joining(StringPool.COMMA, StringPool.LEFT_BRACKET, StringPool.RIGHT_BRACKET));}}
使用示例
productRelService.list(new CombinationLambdaQueryChainWrapper<ProductRel>().select(ProductRel::getLevelId).eq(ProductRel::getLevelId, "GBZ0555A0002").combinationIn(true, Arrays.asList(Tuple.of("91cf1c0f3b0bc46e0238dc33717dc88e", "GBZ0555A0002")), ProductRel::getProductUuid, ProductRel::getLevelId));productRelService.list(new CombinationLambdaQueryChainWrapper<ProductRel>().combinationIn(true, Arrays.asList(Tuple.of(1L, "GBZ0555A0002")), ProductRel::getId, ProductRel::getLevelId));
相关文章:
mybatis多条件in查询拓展
背景 最近碰上有个业务,查询的sql如下: select * from table where (sku_id,batch_no) in ((#{skuId},#{batchNo}),...); 本来也没什么,很简单常见的一种sql。问题是我们使用的是mybatis-plus,然后写的时候又没有考虑到后面的查…...
<Rust><iced>基于rust使用iced构建GUI实例:一个CRC16校验码生成工具
前言 本专栏是Rust实例应用。 环境配置 平台:windows 软件:vscode 语言:rust 库:iced、iced_aw 概述 本文是专栏第五篇实例,是一个CRC16校验码转换程序。 本篇内容: 1、CRC16校验码生成 代码介绍 本文的crc16校验码生成工具,主要设计两个方面,一个是crc16 modbus…...
动态规划与0/1背包问题:深入解析
目录 一、动态规划简介 二、0/1背包问题概述 三、动态规划解决0/1背包问题 1. 定义子问题 2. 确定状态 3. 初始条件和边界情况 4. 计算最终结果 5. 代码实现 6. 空间优化 四、例题讲解 例题1:基础例题 例题2:路径恢复 例题3:扩展…...
Python爬虫:下载人生格言
Python爬虫:下载人生格言 爬取网页 将这些格言下载存储到本地 代码: import requests #导入requests库,用于提取网页 from lxml import etree#导入lxml库,用于Xpath数据解析#请求头 header{ user-agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) A…...
使用注意力机制的seq2seq
一、背景 1、机器翻译中,每个生成的词可能相关于源句子中不同的词,但是之前用的是最后一个RNN层出来的context。 2、加入注意力 (1)假设输入序列中有𝑇个词元, 解码时间步𝑡′的上下文变量是…...
我们的前端开发逆天了!1 小时搞定了新网站,还跟我说 “不要钱”
大家好,我是程序员鱼皮。前段时间我们上线了一个新软件 剪切助手 ,并且针对该项目做了一个官网: 很多同学表示官网很好看,还好奇是怎么做的,其实这个网站的背后还有个有趣的小故事。。。 鱼皮:我们要做个官…...
.NET 相关概念
.NET 和 .NET SDK .NET 介绍 .NET 是一个由 Microsoft 开发和维护的广泛用于构建各种类型应用程序的开发框架。它是一个跨平台、跨语言的开发平台,提供了丰富的类库、API和开发工具,支持开发者使用多种编程语言(如C#、VB.NET、F#等…...
Kubernetes 从集群中移除一个节点(Node)
目录 1. 移除工作节点(Worker Node)1.1 确定工作节点名称1.2 驱逐工作节点上的Pod1.3 删除工作节点1.4 重置该工作节点 2. 移除控制平面节点(Control Plane Node)2.1 确定控制平面节点名称2.2 驱逐控制平面节点上的Pod2.3 更新 etcd 集群2.4 从集群中删除控制平面节点2.5 重置移…...
高德地图离线版 使用高德地图api的方法
高德离线包我已经存至Gitee(自行下载即可):高德地图离线解决方案: 高德地图离线解决方案 然因为高德地图的瓦片地图太大,所以要让后端部署下 前端直接调用 如果本地 直接找到瓦片图路径就可以 initMap () {const base_url "…...
springboot 集成私有化Ollama大模型开源框架,搭建AI智能平台
Ollama是一个用于大数据和机器学习的平台,它可以帮助企业进行数据处理、分析和决策制定。 1、在Spring Boot项目pom.xml中添加Ollama客户端库依赖 <dependency><groupId>org.springframework.ai</groupId><artifactId>spring-a…...
6.key的层级结构
redis的key允许多个单词形成层级结构,多个单词之间用:隔开,格式如下: 项目名:业务名:类型:id 这个格式并非固定的,可以根据自己的需求来删除或添加词条。 例如: taobao:user:1 taobao:product:1 如果value是一个java对…...
LogonTracer图形化事件分析工具
LogonTracer这款工具是基于Python编写的,并使用Neo4j作为其数据库(Neo4j多用于图形数据库),是一款用于分析Windows安全事件登录日志的可视化工具。它会将登录相关事件中的主机名(或IP地址)和帐户名称关联起…...
【云原生】Prometheus监控Docker指标并接入Grafana
目录 一、前言 二、docker监控概述 2.1 docker常用监控指标 2.2 docker常用监控工具 三、CAdvisor概述 3.1 CAdvisor是什么 3.2 CAdvisor功能特点 3.3 CAdvisor使用场景 四、CAdvisor对接Prometheus与Grafana 4.1 环境准备 4.2 docker部署CAdvisor 4.2.2 docker部署…...
搭建日志系统ELK(二)
搭建日志系统ELK(二) 架构设计 在搭建以ELK为核心的日志系统时,Logstash作为日志采集的核心组件,负责将各个服务的日志数据采集、清洗、过滤。然而缺点也很明显: 占用较多的服务器资源。配置复杂,学习曲线陡峭。处理大数据量时…...
常用排序算法的实现与介绍
常用排序算法的实现与介绍 在计算机科学中,排序算法是非常基础且重要的一类算法。本文将通过C语言代码实现,介绍几种常见的排序算法,包括冒泡排序、选择排序、插入排序和快速排序。以下是这些排序算法的具体实现和简要介绍。 1. 冒泡排序&am…...
仓颉语言 -- 宏
使用新版本 (2024-07-19 16:10发布的) 1、宏的简介 宏可以理解为一种特殊的函数。一般的函数在输入的值上进行计算,然后输出一个新的值,而宏的输入和输出都是程序本身。在输入一段程序(或程序片段,例如表达…...
Nginx代理minIO图片路径实现公网图片访问
1、网络部署情况 VUE前端项目Nginx部署在公司内网,端口7790 后台接口项目部署在公司内网,端口7022 minIO服务部署在公司内网,端口9000 公网IP设备将80端口映射到7790端口(具体映射方式不详),实现通过互…...
从零开始掌握tcpdump:参数详解
Linux tcpdump命令详解 1. 语法 tcpdump [-adeflnnNOpqStvxX] [-c <数据包数目>] [-dd] [-ddd] [-F <表达文件>] [-i <网络界面>] [-r <数据包文件>] [-s <数据包大小>] [-tt] [-T <数据包类型>] [-vv] [-w <数据包文件>] [输出数…...
漏洞挖掘 | edusrc记一次某中学小程序渗透测试
一、搜集渗透目标 现在的EDU挖web端的上分效率远不如小程序,因此这篇文章浅浅记录一次小程序的挖掘吧。如果各位大牛想要快速出洞,不妨跳过大学,学院等小程序,而重点关注小学、中学、幼儿园等,这些小程序的出洞率还是…...
vulhub:nginx解析漏洞CVE-2013-4547
此漏洞为文件名逻辑漏洞,该漏洞在上传图片时,修改其16进制编码可使其绕过策略,导致解析为 php。当Nginx 得到一个用户请求时,首先对 url 进行解析,进行正则匹配,如果匹配到以.php后缀结尾的文件名ÿ…...
vLLM Semantic Router:基于信号驱动的LLM智能路由架构与生产实践
1. 项目概述:为什么我们需要一个“智能”的LLM路由器?在当前的LLM应用开发中,我们正面临一个甜蜜的烦恼:模型太多了。从闭源的GPT-4、Claude,到开源的Llama、Qwen、DeepSeek,再到各种针对特定任务微调的小模…...
自动化生产管理平台(Automatic)
1,自动化生产管理平台(Automatic) 1.1,重新定义Window样式 添加WindowChrome元素进行自定义定义 <Window x:Class"lzg.Automatic.MainWindow"xmlns"http://schemas.microsoft.com/winfx/2006/xaml/presentation"xmlns:x"…...
Apple Watch深度体验:从传感器融合到物联网节点的技术实践
1. 从怀疑到依赖:一个技术编辑的Apple Watch真实体验说实话,一开始我压根没打算写这篇关于Apple Watch的东西。作为一名在技术媒体圈混了十多年的老编辑,我太清楚这里面的“坑”了——只要你写点苹果产品的好话,就容易被贴上“果粉…...
企业微信代开发应用:CallBackUrl验证失败排查与CorpID加密升级实战
1. 企业微信代开发应用验证失败的典型场景 最近不少服务商朋友反馈,代开发应用在验证CallBackUrl时频繁失败。这个问题其实源于企业微信在2022年6月底进行的一次安全升级。当时官方发布公告称,为了提升账户安全性,所有新建的代开发应用都需要…...
Azure Quickstart Templates 多区域部署高可用架构设计终极指南:5步构建企业级灾难恢复方案
Azure Quickstart Templates 多区域部署高可用架构设计终极指南:5步构建企业级灾难恢复方案 【免费下载链接】azure-quickstart-templates Azure Quickstart Templates 项目地址: https://gitcode.com/gh_mirrors/az/azure-quickstart-templates 在当今数字化…...
scp 命令的使用方法 什么软件支持 .git bash xshell .openssh
scp 命令的使用方法 什么软件支持 .git bash xshell .openssh scp backup.sh deploy.sh rollback.sh userserver:/path/to/project/ 这个命令主要在 Linux、macOS 或 Windows (10/11) 的 命令行终端(Terminal / Command Prompt / PowerShellÿ…...
Neo-Launcher数据库架构:数据存储和管理的深度解析
Neo-Launcher数据库架构:数据存储和管理的深度解析 【免费下载链接】Neo-Launcher Neo-Launcher 项目地址: https://gitcode.com/gh_mirrors/ne/Neo-Launcher Neo-Launcher是一款由Neo Collective开发的开源启动器应用,其高效的数据存储和管理系统…...
Vibe Annotations:AI编程时代的视觉反馈工具,精准沟通前端修改意图
1. 项目概述:一个为AI编程时代量身定制的视觉反馈工具如果你和我一样,每天都在和AI编程助手(比如Cursor、Claude Code)打交道,那你肯定遇到过这个痛点:想让它帮你改一个网页按钮的颜色,或者调整…...
开关电源EMC设计:从原理到实践的关键技术
1. 开关电源EMC设计基础 开关电源因其高效率和小型化优势,在现代电子设备中广泛应用。然而,高频开关动作带来的电磁干扰(EMI)问题不容忽视。作为一名电源工程师,我经常需要面对各种EMC挑战。记得有一次,我们团队设计的工业电源模块…...
WaveTools:鸣潮玩家的终极优化工具箱,轻松解锁120FPS流畅体验
WaveTools:鸣潮玩家的终极优化工具箱,轻松解锁120FPS流畅体验 【免费下载链接】WaveTools 🧰鸣潮工具箱 项目地址: https://gitcode.com/gh_mirrors/wa/WaveTools 你是否曾经在《鸣潮》的激烈战斗中感受到画面卡顿?是否因为…...
