From dcd36bf108598d023391c67936d27b82119933ab Mon Sep 17 00:00:00 2001 From: Mark Paluch Date: Tue, 10 Dec 2024 15:17:37 +0100 Subject: [PATCH] Use primary table alias for SQL count query derivation with DISTINCT queries. We now use render COUNT(DISTINCT a.*) where 'a' is the primary table alias instead of COUNT(DISTINCT *). Closes #3707 --- .../query/JSqlParserQueryEnhancer.java | 12 +++++++----- .../query/JSqlParserQueryEnhancerUnitTests.java | 16 ++++++++++++++++ 2 files changed, 23 insertions(+), 5 deletions(-) diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JSqlParserQueryEnhancer.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JSqlParserQueryEnhancer.java index 37ec06e12..168614860 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JSqlParserQueryEnhancer.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JSqlParserQueryEnhancer.java @@ -354,11 +354,11 @@ public class JSqlParserQueryEnhancer implements QueryEnhancer { return this.query.getQueryString(); } - return createCountQueryFor(this.query, selectBody, countProjection); + return createCountQueryFor(this.query, selectBody, countProjection, primaryAlias); } private static String createCountQueryFor(DeclaredQuery query, PlainSelect selectBody, - @Nullable String countProjection) { + @Nullable String countProjection, @Nullable String primaryAlias) { // remove order by selectBody.setOrderByElements(null); @@ -373,7 +373,8 @@ public class JSqlParserQueryEnhancer implements QueryEnhancer { selectBody.setDistinct(null); // reset possible distinct Function jSqlCount = getJSqlCount( - Collections.singletonList(countPropertyNameForSelection(selectBody.getSelectItems(), distinct)), distinct); + Collections.singletonList(countPropertyNameForSelection(selectBody.getSelectItems(), distinct, primaryAlias)), + distinct); selectBody.setSelectItems(Collections.singletonList(SelectItem.from(jSqlCount))); } @@ -463,7 +464,8 @@ public class JSqlParserQueryEnhancer implements QueryEnhancer { * @param tableAlias the table alias which can be {@literal null}. * @return */ - private static String countPropertyNameForSelection(List> selectItems, boolean distinct) { + private static String countPropertyNameForSelection(List> selectItems, boolean distinct, + @Nullable String tableAlias) { if (onlyASingleColumnProjection(selectItems)) { @@ -472,7 +474,7 @@ public class JSqlParserQueryEnhancer implements QueryEnhancer { return column.getFullyQualifiedName(); } - return (distinct ? "*" : "1"); + return distinct ? ((tableAlias != null ? tableAlias + "." : "") + "*") : "1"; } /** diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/JSqlParserQueryEnhancerUnitTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/JSqlParserQueryEnhancerUnitTests.java index a41b54193..7ed0a804c 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/JSqlParserQueryEnhancerUnitTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/JSqlParserQueryEnhancerUnitTests.java @@ -51,6 +51,22 @@ public class JSqlParserQueryEnhancerUnitTests extends QueryEnhancerTckTests { assertThat(sql).isEqualTo("SELECT e FROM Employee e ORDER BY e.foo ASC, e.bar ASC"); } + @Test // GH-3707 + void countQueriesShouldConsiderPrimaryTableAlias() { + + QueryEnhancer enhancer = createQueryEnhancer(DeclaredQuery.of(""" + SELECT DISTINCT a.*, b.b1 + FROM TableA a + JOIN TableB b ON a.b = b.b + LEFT JOIN TableC c ON b.c = c.c + ORDER BY b.b1, a.a1, a.a2 + """, true)); + + String sql = enhancer.createCountQueryFor(); + + assertThat(sql).startsWith("SELECT count(DISTINCT a.*) FROM TableA a"); + } + @Override @ParameterizedTest // GH-2773 @MethodSource("jpqlCountQueries")