JPA Querydsl多条件排序问题:如何实现年龄升序+用户名nullsLast排序?
我有一张名为Member的简单数据表,结构如下:
| id | username | age |
|---|---|---|
| 1 | member17 | 25 |
| 2 | member18 | 24 |
| 3 | member19 | 23 |
| 4 | member20 | 22 |
| 5 | null | 21 |
| 6 | member22 | 21 |
| 7 | member23 | 21 |
| 8 | member24 | 18 |
| 9 | null | 17 |
我正在练习使用Querydsl进行排序,编写了如下查询代码:
@Override public List<Member> sortMemberList() { return queryFactory .selectFrom(member) .where(member.age.between(17, 25)) .orderBy( member.age.asc(), member.username.asc().nullsLast() ) .fetch(); }
该查询生成的SQL语句如下:
select m1_0.member_id, m1_0.age, m1_0.team_id, m1_0.username from Member m1_0 where m1_0.age between ? and ? order by m1_0.age, case when (m1_0.username) is null then 1 else 0 end, m1_0.username
但查询结果中member.username.asc().nullsLast()并未生效:
[ { "username": null, "age": 17 }, { "username": "member24", "age": 18 }, { "username": "member22", "age": 21 }, { "username": "member23", "age": 21 }, { "username": null, "age": 21 }, { "username": "member20", "age": 22 }, { "username": "member19", "age": 23 }, { "username": "member18", "age": 24 }, { "username": "member17", "age": 25 } ]
当我调换orderBy的条件顺序为:
.orderBy( member.username.asc().nullsLast(), member.age.asc() )
此时用户名排序生效,但年龄排序被忽略:
[ { "username": "member17", "age": 25 }, { "username": "member18", "age": 24 }, { "username": "member19", "age": 23 }, { "username": "member20", "age": 22 }, { "username": "member22", "age": 21 }, { "username": "member23", "age": 21 }, { "username": "member24", "age": 18 }, { "username": null, "age": 17 }, { "username": null, "age": 21 } ]
若想先应用年龄排序条件,再应用用户名排序条件,直接使用member.username.nullsLast()会出现Java编译错误:
java: cannot find symbol symbol: method nullsLast() location: variable username of type com.querydsl.core.types.dsl.StringPath
请问如何实现先按member.age.asc()排序,同时对username应用nullsLast的排序需求?
1. 初始查询已符合需求
仔细观察第一个查询的结果:
- 所有记录先按
age升序排列,age=17的记录(即使username为null)排在最前面,符合先按age排序的优先级规则; - 同年龄组内(比如
age=21的三条记录),username按升序排列且null值放在组内最后,顺序为member22→member23→null,这正是member.username.asc().nullsLast()的预期效果。
你误以为“未生效”是混淆了全局null排序和同组内null排序的逻辑,nullsLast()仅作用于当前排序条件的分组内,而非全局所有记录。
2. 若需全局null记录排在最后(同时保留age升序)
如果真实需求是:所有username为null的记录不管age大小都排在最后,再按age升序、username升序排列,可调整排序逻辑如下:
.orderBy( member.username.isNull().asc(), // 非null记录排前面,null排后面 member.age.asc(), member.username.asc() )
这里利用username.isNull()返回布尔值,升序时false(非null)会排在true(null)之前,实现全局null后置的效果。
3. 编译错误说明
直接调用member.username.nullsLast()编译失败,是因为nullsLast()是OrderSpecifier接口的方法,而StringPath本身没有这个方法。必须先调用asc()或desc()生成OrderSpecifier对象后,才能调用nullsLast(),所以你最初的member.username.asc().nullsLast()写法是完全正确的。
内容的提问来源于stack exchange,提问作者Inseok Chang

