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后缀结尾的文件名ÿ…...
脑机新手指南(八):OpenBCI_GUI:从环境搭建到数据可视化(下)
一、数据处理与分析实战 (一)实时滤波与参数调整 基础滤波操作 60Hz 工频滤波:勾选界面右侧 “60Hz” 复选框,可有效抑制电网干扰(适用于北美地区,欧洲用户可调整为 50Hz)。 平滑处理&…...
R语言AI模型部署方案:精准离线运行详解
R语言AI模型部署方案:精准离线运行详解 一、项目概述 本文将构建一个完整的R语言AI部署解决方案,实现鸢尾花分类模型的训练、保存、离线部署和预测功能。核心特点: 100%离线运行能力自包含环境依赖生产级错误处理跨平台兼容性模型版本管理# 文件结构说明 Iris_AI_Deployme…...
逻辑回归:给不确定性划界的分类大师
想象你是一名医生。面对患者的检查报告(肿瘤大小、血液指标),你需要做出一个**决定性判断**:恶性还是良性?这种“非黑即白”的抉择,正是**逻辑回归(Logistic Regression)** 的战场&a…...
Golang dig框架与GraphQL的完美结合
将 Go 的 Dig 依赖注入框架与 GraphQL 结合使用,可以显著提升应用程序的可维护性、可测试性以及灵活性。 Dig 是一个强大的依赖注入容器,能够帮助开发者更好地管理复杂的依赖关系,而 GraphQL 则是一种用于 API 的查询语言,能够提…...
Cinnamon修改面板小工具图标
Cinnamon开始菜单-CSDN博客 设置模块都是做好的,比GNOME简单得多! 在 applet.js 里增加 const Settings imports.ui.settings;this.settings new Settings.AppletSettings(this, HTYMenusonichy, instance_id); this.settings.bind(menu-icon, menu…...
C# 类和继承(抽象类)
抽象类 抽象类是指设计为被继承的类。抽象类只能被用作其他类的基类。 不能创建抽象类的实例。抽象类使用abstract修饰符声明。 抽象类可以包含抽象成员或普通的非抽象成员。抽象类的成员可以是抽象成员和普通带 实现的成员的任意组合。抽象类自己可以派生自另一个抽象类。例…...
【决胜公务员考试】求职OMG——见面课测验1
2025最新版!!!6.8截至答题,大家注意呀! 博主码字不易点个关注吧,祝期末顺利~~ 1.单选题(2分) 下列说法错误的是:( B ) A.选调生属于公务员系统 B.公务员属于事业编 C.选调生有基层锻炼的要求 D…...
C++ 求圆面积的程序(Program to find area of a circle)
给定半径r,求圆的面积。圆的面积应精确到小数点后5位。 例子: 输入:r 5 输出:78.53982 解释:由于面积 PI * r * r 3.14159265358979323846 * 5 * 5 78.53982,因为我们只保留小数点后 5 位数字。 输…...
k8s业务程序联调工具-KtConnect
概述 原理 工具作用是建立了一个从本地到集群的单向VPN,根据VPN原理,打通两个内网必然需要借助一个公共中继节点,ktconnect工具巧妙的利用k8s原生的portforward能力,简化了建立连接的过程,apiserver间接起到了中继节…...
关键领域软件测试的突围之路:如何破解安全与效率的平衡难题
在数字化浪潮席卷全球的今天,软件系统已成为国家关键领域的核心战斗力。不同于普通商业软件,这些承载着国家安全使命的软件系统面临着前所未有的质量挑战——如何在确保绝对安全的前提下,实现高效测试与快速迭代?这一命题正考验着…...
