MyBatis Plus 按指定顺序查询对象列表

场景

定义了一个字段,存储了一个 json 数组比如:[41,38,42],它的含义是一个线性的流程定义,所以保证顺序至关重要

现在使用 MyBatis Plus 的 API 方法去通过 ID 数组查询得到对象数组

List<ProcessNodePO> processNodeList = processNodeMapper.selectList(new LambdaQueryWrapper<ProcessNodePO>()
        .in(ProcessNodePO::getProcessId, processIdList));

问题

原 ID 数组中的顺序为:41,38,42

查询得到的对象 ID 顺序为:38,41,42

顺序被改写,对象数组顺序与参数数组顺序不一致

这个问题的本质是 SQL 语句中的IN()造成的

目标

使得查询得到的对象数组顺序与原参数数组保持一致

解决方案

方案 1:MyBatis plus last()

last()用于在查询末尾添加自定义 SQL,但是代码和 SQL 语句混在一起观感不好、也不利于维护,而且通过字符串来拼接 SQL 也会存在 SQL 注入的风险

List<Integer> processIdList = Arrays.asList(41, 38, 42);

LambdaQueryWrapper<ProcessNodePO> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.in(ProcessNodePO::getProcessId, processIdList);

// 使用 FIELD 函数在 SQL 中指定排序顺序
String fieldOrder = processIdList.stream()
                                 .map(String::valueOf)
                                 .collect(Collectors.joining(","));
queryWrapper.last("ORDER BY FIELD(process_id, " + fieldOrder + ")");

List<ProcessNodePO> processNodeList = processNodeMapper.selectList(queryWrapper);

方案二:用 Java 代码对查询出的集合重新排序

这个方式也不好

List<ProcessNodePO> processNodeList = processNodeMapper.selectList(queryWrapper);

// 使用给定的顺序排序结果
Map<Integer, Integer> orderMap = new HashMap<>();
for (int i = 0; i < processIdList.size(); i++) {
    orderMap.put(processIdList.get(i), i);
}

// 按照 orderMap 排序
processNodeList.sort(Comparator.comparingInt(po -> orderMap.getOrDefault(po.getProcessId(), Integer.MAX_VALUE)));

方案三:用 MyBatis 动态 SQL

当然,这本质上和方案一没有区别,都是使用FIELD(),但是形式上好得多

<select id="queryListByDesignatedOrder" parameterType="java.lang.Integer"
        resultType="com.xingyun.warning.center.infrastructure.db.dataObject.helper.ProcessNodePO">
    SELECT process_id,process_desc,is_input_required as inputRequired,param_name,prompt,is_request_required as
    sendRequestRequired,component
    FROM t_operation_helper_process_node
    WHERE process_id IN
    <foreach item="id" collection="nodeIdList" open="(" separator="," close=")">
        #{id}
    </foreach>
    ORDER BY FIELD(
    process_id,
    <foreach item="id" collection="nodeIdList" separator=",">
        #{id}
    </foreach>
    )
</select>
posted @ 2024-04-25 10:31  YaosGHC  阅读(423)  评论(0编辑  收藏  举报