Skip to content

Commit 463b54a

Browse files
committed
Properly handle Sort's that start with a join alias.
JOIN clauses can have aliases as well, despite not using an AS reserved word. The HQL query parser needs to handle this. See #2960, #1066, #664.
1 parent 3393771 commit 463b54a

File tree

3 files changed

+53
-2
lines changed

3 files changed

+53
-2
lines changed

spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/HqlQueryTransformer.java

+24
Original file line numberDiff line numberDiff line change
@@ -280,6 +280,30 @@ public List<JpaQueryParsingToken> visitJoin(HqlParser.JoinContext ctx) {
280280
return tokens;
281281
}
282282

283+
@Override
284+
public List<JpaQueryParsingToken> visitJoinPath(HqlParser.JoinPathContext ctx) {
285+
286+
List<JpaQueryParsingToken> tokens = super.visitJoinPath(ctx);
287+
288+
if (ctx.variable() != null) {
289+
transformerSupport.registerAlias(tokens.get(tokens.size() - 1).getToken());
290+
}
291+
292+
return tokens;
293+
}
294+
295+
@Override
296+
public List<JpaQueryParsingToken> visitJoinSubquery(HqlParser.JoinSubqueryContext ctx) {
297+
298+
List<JpaQueryParsingToken> tokens = super.visitJoinSubquery(ctx);
299+
300+
if (ctx.variable() != null) {
301+
transformerSupport.registerAlias(tokens.get(tokens.size() - 1).getToken());
302+
}
303+
304+
return tokens;
305+
}
306+
283307
@Override
284308
public List<JpaQueryParsingToken> visitAlias(HqlParser.AliasContext ctx) {
285309

spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryTransformerSupport.java

+6-1
Original file line numberDiff line numberDiff line change
@@ -124,11 +124,16 @@ private boolean shouldPrefixWithAlias(Sort.Order order) {
124124
return false;
125125
}
126126

127-
// If the Sort references an alias
127+
// If the Sort references an alias directly
128128
if (projectionAliases.contains(order.getProperty())) {
129129
return false;
130130
}
131131

132+
// If the Sort property starts with an alias
133+
if (projectionAliases.stream().anyMatch(alias -> order.getProperty().startsWith(alias))) {
134+
return false;
135+
}
136+
132137
return true;
133138
}
134139
}

spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/HqlQueryTransformerTests.java

+23-1
Original file line numberDiff line numberDiff line change
@@ -250,7 +250,7 @@ void doesNotPrefixOrderReferenceIfOuterJoinAliasDetected() {
250250

251251
String query = "select p from Person p left join p.address address";
252252
Sort sort = Sort.by("address.city");
253-
assertThat(createQueryFor(query, sort)).endsWith("order by p.address.city asc");
253+
assertThat(createQueryFor(query, sort)).endsWith("order by address.city asc");
254254
}
255255

256256
@Test // DATAJPA-252
@@ -966,6 +966,28 @@ void shouldHandleAliasInsideCaseStatement() {
966966
"order by newDateDue desc");
967967
}
968968

969+
@Test // GH-2960
970+
void sortingRecognizesJoinAliases() {
971+
972+
String query = "select p from Customer c join c.productOrder p where p.delayed = true";
973+
974+
PageRequest page = PageRequest.of(0, 20, Sort.Direction.DESC, "lastName");
975+
assertThat(createQueryFor(query, page.getSort())).isEqualToIgnoringWhitespace("""
976+
select p from Customer c
977+
join c.productOrder p
978+
where p.delayed = true
979+
order by c.lastName desc
980+
""");
981+
982+
PageRequest page2 = PageRequest.of(0, 20, Sort.Direction.DESC, "p.lineItems");
983+
assertThat(createQueryFor(query, page2.getSort())).isEqualToIgnoringWhitespace("""
984+
select p from Customer c
985+
join c.productOrder p
986+
where p.delayed = true
987+
order by p.lineItems desc
988+
""");
989+
}
990+
969991
private void assertCountQuery(String originalQuery, String countQuery) {
970992
assertThat(createCountQueryFor(originalQuery)).isEqualTo(countQuery);
971993
}

0 commit comments

Comments
 (0)