MybatisPlus使用排序查询时,将null值放到最后

news/2024/11/15 18:17:27/

1用户需求

查询结果,按照某些字段进行排序,将为null的值放到最后。按照更新时间排序,但是更新时间可能为null,因此将null的数据放到最后。

2解决方案

最简单的方式,当然是下面这种直接在SQL最后面 NULLS LAST ,但是问题是,我都用MybatisPlus,下面的这种SQL那肯定不会写了啊,要是用MybatisPlus还写下面这种单表SQL的查询的,我建议可以放弃MybatisPlus了

SELECT * FROM users ORDER BY OPERATE_DATE ASC NULLS LAST 

先说最终解决方案,用mybatis拦截器修改最终执行的sql语句

思路就是将queryWrapper构造的SQL语句中的ASC替换成ASC NULLS LAST即使用queryWrapper的orderBy时,mybatis-plus会生成这个SQL语句
SELECT * FROM users ORDER BY OPERATE_DATE ASC而我们要做的就是在mybatis-plus执行之前,将ASC变成 ASC NULLS LAST 

下面是我们进行排序的代码。目前来看,我们只能改这里,不过查找了一圈,都没有解决方案,因此放弃,用另外拦截器的方式实现。

if(!ObjectUtils.isEmpty(orderBy)) {if(orderBy instanceof Collection) {String[] array = ((Collection<?>) orderBy).toArray(new String[0]);queryWrapper.orderBy(true, isAsc, Arrays.asList(array));}else {queryWrapper.orderBy(true, isAsc, orderBy.toString());}
}

当然GPT一本正经的胡说八道,看着挺像回事的,可惜mybait-plus没有这个方法,所以看看就好。

orderByAscWithNullsLast()

在这里插入图片描述

3拦截器代码

这里开始,就是最后的代码实现了

3.1编写拦截器LastNullInterceptor

import java.lang.reflect.Field;
import java.sql.Connection;import org.apache.ibatis.executor.statement.StatementHandler;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.SqlCommandType;
import org.apache.ibatis.mapping.StatementType;
import org.apache.ibatis.plugin.Interceptor;
import org.apache.ibatis.plugin.Intercepts;
import org.apache.ibatis.plugin.Invocation;
import org.apache.ibatis.plugin.Signature;
import org.apache.ibatis.reflection.MetaObject;
import org.apache.ibatis.reflection.SystemMetaObject;
import org.springframework.util.ReflectionUtils;import com.baomidou.mybatisplus.core.toolkit.PluginUtils;/*** @description:拦截查询SQL,处理查询SQL中的排序* @author:hutao* @mail:hutao_2017@aliyun.com* @date:2023年7月25日 下午12:17:50*/
@Intercepts(@Signature(type = StatementHandler.class, method = "prepare", args = {Connection.class, Integer.class}))
public class LastNullInterceptor implements Interceptor {//,有兴趣,可以看看MybatisPlusInterceptor怎么实现的private static final String DESC = "DESC";private static final String ASC = "ASC";private static final String REPLACE_DESC = "DESC NULLS LAST";private static final String REPLACE_ASC = "ASC NULLS LAST";@Overridepublic Object intercept(Invocation invocation) throws Throwable {StatementHandler handler = PluginUtils.realTarget(invocation.getTarget());MetaObject metaObject = SystemMetaObject.forObject(handler);// 判断是不是SELECT操作,跳过存储过程MappedStatement mappedStatement = (MappedStatement) metaObject.getValue("delegate.mappedStatement");if (SqlCommandType.SELECT != mappedStatement.getSqlCommandType()|| StatementType.CALLABLE == mappedStatement.getStatementType()) {return invocation.proceed();}BoundSql boundSql = handler.getBoundSql();String sql = boundSql.getSql().toUpperCase();if(sql.contains("ORDER BY")) {sql = this.replaceLast(sql, DESC, REPLACE_DESC);sql = this.replaceLast(sql, ASC, REPLACE_ASC);Field sqlField = boundSql.getClass().getDeclaredField("sql");ReflectionUtils.makeAccessible(sqlField);ReflectionUtils.setField(sqlField, boundSql, sql);}return invocation.proceed();}/*** @description:替换最后一个字符串* @author:hutao* @mail:hutao1@epri.sgcc.com.cn* @date:2023年7月25日 下午2:22:21*/public String replaceLast(String str, String target, String replacement) {if (str == null || target == null || replacement == null) {return str;}int lastIndex = str.lastIndexOf(target);if (lastIndex < 0) {return str;}return str.substring(0, lastIndex) + replacement + str.substring(lastIndex + target.length());}
}

3.2注入拦截器

@SpringBootConfiguration
public class MybatisConfig {@Beanpublic LastNullInterceptor nullsLastInterceptor() {return new LastNullInterceptor();}
}

4结果展示

打印mybatis-plus的sql,我们可以发现,已经将ASC替换成 ASC NULLS LAST了
在这里插入图片描述


http://www.ppmy.cn/news/977710.html

相关文章

UI 自动化稳定性用例实战经验分享!

目录 前言&#xff1a; 大家常说 UI 自动化不稳定&#xff0c;那又如何提高稳定性呢&#xff1f; 操作界面非预期的弹框、广告、浮层 测试系统的 A/B 策略 总结&#xff1a; 前言&#xff1a; 稳定性测试是软件测试的一个重要方面&#xff0c;它旨在评估软件在不同负载和…

Go 语言 值类型和引用类型

Go 语言 值类型和引用类型 值类型&#xff1a; 概述&#xff1a; 值类型的人变量直接存储其值&#xff0c;他们通常在栈上分配内存。当把一个值类型的变量赋值给另外一个变量、作为函数参数传递或从函数返回时&#xff0c;进行值的复制。因此每个变量都有自己独立的存储&…

2023 年牛客多校第一场题解(下)

I Random 题意&#xff1a;给定对 x x x 进行 m m m 次左移/右移并异或的函数 rand ( x ) \text{rand}(x) rand(x)&#xff0c;问期望对 [ 0 , 2 n − 1 ] [0,2^n-1] [0,2n−1] 上均匀随机分布的 x x x 执行多少次 rand \text{rand} rand 可以变回 x x x 本身。 1 ≤ n…

sketch如何在线打开?有没有什么软件可以辅助

Sketch 在线打开的方法有哪些&#xff1f;这个问题和我之前回答过的「Sketch 可以在线编辑吗&#xff1f;」是一样的答案&#xff0c;没有。很遗憾&#xff0c;Sketch 没有在线打开的方法&#xff0c;Sketch 也做不到可以在线编辑。那么&#xff0c;那些广告里出现的设计软件工…

基于Java+SpringBoot+vue前后端分离在线商城系统设计实现

博主介绍&#xff1a;✌全网粉丝30W,csdn特邀作者、博客专家、CSDN新星计划导师、Java领域优质创作者,博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战✌ &#x1f345;文末获取源码联系&#x1f345; &#x1f447;&#x1f3fb; 精彩专…

在setup中使用$refs的方法

在setup中使用$refs的方法 setup的第二个参数context提供了一个上下文对象,context作为上下文取代this&#xff0c;但是context中只有emit&#xff0c;attrs,和slots。 在非setup钩子中, 我们都是通过this.$refs来获取指定元素。 setup中使用this.$refs的步骤 定义一个ref变…

前端随笔:HTML/CSS/JavaScript和Vue

前端随笔 1&#xff1a;HTML、JavaScript和Vue 最近因为工作需要&#xff0c;需要接触一些前端的东西。之前虽然大体上了解过HTML、CSS和JavaScript&#xff0c;也知道HTML定义了内容、CSS定义了样式、JavaScript定义了行为&#xff0c;但是却没有详细的学习过前端三件套的细节…

基于IMX6ULL的智能车载终端项目(代码开源)

前言&#xff1a;本文为手把手教学智能车载终端项目&#xff08;LinuxQT&#xff09;&#xff0c;该项目是综合性非常强的 Linux 系列项目&#xff01;项目核心板使用 NXP 的 IMX6ULL 作为 CPU&#xff0c;整体实现了简化版本的车载终端功能需求。项目可以学习的点非常多&#…