Spring Data Sort 转 QueryDSL OrderSpecifier 通用方法

使用场景

在使用 Spring Data JPA + QueryDSL 构建后台分页查询时,前端传入的排序参数(如按某字段升序/降序排列)需要转换为 QueryDSL 的 OrderSpecifier

通常的做法是在每个 Service 中手动转换,或者针对每个实体写专门的转换逻辑,导致大量重复代码。本工具方法封装了通用的转换逻辑,可直接复用。


前端参数格式

前端传递排序参数时,sort 格式为数组,每个元素为 字段名,排序方向

javascript 复制代码
// 单字段排序
sort: ['year,desc']

// 多字段排序(优先级从左到右)
sort: ['year,desc', 'id,desc']

// 升序
sort: ['entryDate,asc']

后端 Spring MVC 通过 Pageable 接收后,自动解析为 org.springframework.data.domain.Sort 对象。


代码实现

PageUtil.java

java 复制代码
package com.server.common.util;

import cn.hutool.core.util.ObjectUtil;
import com.querydsl.core.types.OrderSpecifier;
import com.querydsl.core.types.dsl.*;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

/**
 * 分页工具类
 */
public class PageUtil {

    private static final int DEFAULT_PAGE_SIZE = 10;
    private static final Page<?> EMPTY_PAGE = new PageImpl<>(Collections.emptyList(), PageRequest.of(0, DEFAULT_PAGE_SIZE), 0);

    // ... 其他空分页方法 ...

    /**
     * 将 Spring Data Sort 转换为 QueryDSL 的 OrderSpecifier
     *
     * @param sort           Spring Data Sort 对象
     * @param entityPathBase QueryDSL EntityPathBase(如 QUser.user)
     * @return OrderSpecifier 数组
     */
    public static <T> OrderSpecifier<?>[] toOrderSpecifiers(Sort sort, EntityPathBase<T> entityPathBase) {
        List<OrderSpecifier<?>> orderSpecifiers = new ArrayList<>();

        if (sort.isUnsorted()) {
            return new OrderSpecifier[0];
        }

        PathBuilder<T> pathBuilder = new PathBuilder<>(entityPathBase.getType(), entityPathBase.getMetadata().getName());

        for (Sort.Order order : sort) {
            String property = order.getProperty();
            ComparableExpressionBase<?> path = pathBuilder.getComparable(property, Comparable.class);

            if (order.isAscending()) {
                orderSpecifiers.add(path.asc());
            } else {
                orderSpecifiers.add(path.desc());
            }
        }

        return orderSpecifiers.toArray(new OrderSpecifier[0]);
    }
}

使用示例

Service 实现

java 复制代码
@Override
public Page<UserLeaveQuotaVo> findPageByParam(UserLeaveQuotaParam param, Pageable pageable) {
    QUserLeaveQuota q = QUserLeaveQuota.userLeaveQuota;
    QUser qUser = QUser.user;

    BooleanBuilder predicate = getPredicate(param);

    // 使用 PageUtil.toOrderSpecifiers 转换排序参数
    List<Tuple> tuples = queryFactory
            .select(q, qUser.nickName, qUser.gender)
            .from(q)
            .leftJoin(qUser).on(q.userId.eq(qUser.id))
            .where(predicate)
            .offset(pageable.getOffset())
            .limit(pageable.getPageSize())
            .orderBy(PageUtil.toOrderSpecifiers(pageable.getSort(), q))
            .fetch();

    // count query ...
    return new PageImpl<>(voList, pageable, total);
}

Vue 前端排序处理

javascript 复制代码
const sortStr = ref(['year,desc', 'id,desc'])

const handleSortChange = ({prop, order}) => {
  if (order) {
    const sortOrder = order === 'ascending' ? 'asc' : 'desc'
    sortStr.value = [prop + ',' + sortOrder, 'id,desc']
  } else {
    sortStr.value = ['year,desc', 'id,desc']
  }
  loadData()
}

const loadData = async () => {
  const params = {
    ...queryParams.value,
    page: currentPage.value - 1,
    size: pageSize.value,
    sort: sortStr.value
  }
  const data = await api.page(params)
  // ...
}

关键点说明

要点 说明
EntityPathBase 直接传入 QueryDSL 生成的 Q 类(如 QUser.user),利用其元数据构建 PathBuilder
getComparable 解决泛型 Comparable 约束问题,避免强制类型转换
isUnsorted 如果前端未传入排序参数,直接返回空数组
返回类型 OrderSpecifier[],直接可用于 QueryDSL 的 orderBy() 方法

环境版本

组件 版本
QueryDSL 5.0.0
Spring Data JPA 3.x
Hibernate 6.x
相关推荐
刘大猫.4 天前
java工具:《List集合按照某个属性值又打大小排序》
comparator·sort·自定义排序·集合排序·降序排列·list排序·list集合排序
醉颜凉2 个月前
深入理解【插入排序】:原理、实现与优化
算法·排序算法·插入排序·sort
源代码•宸3 个月前
Leetcode—39. 组合总和【中等】
经验分享·算法·leetcode·golang·sort·slices
汉克老师3 个月前
GESP2025年12月认证C++四级真题与解析(编程题2 (优先购买))
c++·sort·结构体·优先级·gesp4级·gesp四级
liwenzhen20053 个月前
DM SQL 排序优化-消除排序
排序·sort·dm
西京刀客3 个月前
go语言-切片排序之sort.Slice 和 sort.SliceStable 的区别(数据库分页、内存分页场景注意点)
后端·golang·sort·数据库分页·内存分页
Irene19913 个月前
JavaScript 原生 sort() 方法详解
排序·sort
huisheng_qaq7 个月前
【ElasticSearch实用篇-03】QueryDsl高阶用法以及缓存机制
elasticsearch·缓存·nosql·querydsl·score打分机制
hzhzh~8 个月前
【C++】神秘-希尔排序
c++·希尔排序·排序·sort