方法1:使用 CASE WHEN
语句排序
// ========== 自定义排序逻辑 ==========
// 定义“离线”排在前,“在线”排在后
Expression<Object> caseExpression = cb.selectCase()
.when(cb.equal(root.get("status"), "离线"), 0)
.when(cb.equal(root.get("status"), "在线"), 1)
.otherwise(2); // 其他状态排在最后
Order statusOrder = request.isDesc() ? cb.desc(caseExpression) : cb.asc(caseExpression);
query.orderBy(statusOrder, cb.desc(root.get("updatedTime")));
Expression<Integer> caseExpression = cb.selectCase()
.when(cb.equal(root.get("status"), cb.literal("离线")), 0)
.when(cb.equal(root.get("status"), cb.literal("在线")), 1)
.otherwise(2);
方法 2:自定义排序规则(用 Map
预排序)
如果 status
取值固定(如 "离线"、"在线"),可以在 Java 代码中预处理排序权重,然后传给 JPA:
// 定义状态的排序优先级
Map<String, Integer> statusPriority = Map.of(
"离线", 0,
"在线", 1
);
// 获取 status 排序值
Expression<Integer> statusSort = cb.literal(statusPriority.getOrDefault(root.get("status"), 2));
Order statusOrder = request.isDesc() ? cb.desc(statusSort) : cb.asc(statusSort);
Order timeOrder = cb.desc(root.get("updatedTime"));
query.orderBy(statusOrder, timeOrder);
方法 3:先按 status
分组,再用 updatedTime
排序
List<DynamicDevice> offlineDevices = dynamicDeviceRepo.findAll(
(root, query, cb) -> {
Predicate predicate = cb.equal(root.get("status"), "离线");
query.orderBy(cb.desc(root.get("updatedTime")));
return predicate;
}
);
List<DynamicDevice> onlineDevices = dynamicDeviceRepo.findAll(
(root, query, cb) -> {
Predicate predicate = cb.equal(root.get("status"), "在线");
query.orderBy(cb.desc(root.get("updatedTime")));
return predicate;
}
);
// 合并结果
List<DynamicDevice> sortedDevices = new ArrayList<>();
if (request.isDesc()) {
sortedDevices.addAll(onlineDevices); // 倒序时,在线优先
sortedDevices.addAll(offlineDevices);
} else {
sortedDevices.addAll(offlineDevices); // 默认,离线优先
sortedDevices.addAll(onlineDevices);
}
方案对比
方法 | 适用场景 | 优势 | 劣势 |
---|---|---|---|
方法 1(CASE WHEN) | 数据量大,查询高效 | 数据库直接排序,性能好 | 需要数据库支持 CASE WHEN |
方法 2(Map 预排序) | Hibernate 6+,避免 CASE WHEN | 减少 SQL 计算,更兼容 | 适用于少量状态值 |
方法 3(分组查询 + 合并) | 数据量中等(几万条以内) | 每次查询只查部分数据 | Java 端合并,代码复杂 |