diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/HqlCountQueryTransformer.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/HqlCountQueryTransformer.java index 735bdae29c..6a12eb6e56 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/HqlCountQueryTransformer.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/HqlCountQueryTransformer.java @@ -17,6 +17,8 @@ import static org.springframework.data.jpa.repository.query.QueryTokens.*; +import org.antlr.v4.runtime.ParserRuleContext; +import org.springframework.data.jpa.repository.query.HqlParser.CteContext; import org.springframework.data.jpa.repository.query.HqlParser.SelectClauseContext; import org.springframework.data.jpa.repository.query.QueryRenderer.QueryRendererBuilder; import org.springframework.data.jpa.repository.query.QueryTransformers.CountSelectionTokenStream; @@ -36,6 +38,7 @@ class HqlCountQueryTransformer extends HqlQueryRenderer { private final @Nullable String countProjection; private final @Nullable String primaryFromAlias; + private boolean containsCTE = false; HqlCountQueryTransformer(@Nullable String countProjection, @Nullable String primaryFromAlias) { this.countProjection = countProjection; @@ -66,6 +69,12 @@ public QueryRendererBuilder visitOrderedQuery(HqlParser.OrderedQueryContext ctx) return builder; } + @Override + public QueryTokenStream visitCte(CteContext ctx) { + this.containsCTE = true; + return super.visitCte(ctx); + } + @Override public QueryRendererBuilder visitFromQuery(HqlParser.FromQueryContext ctx) { @@ -189,7 +198,9 @@ public QueryTokenStream visitSelectClause(HqlParser.SelectClauseContext ctx) { nested.append(QueryTokens.expression(ctx.DISTINCT())); nested.append(getDistinctCountSelection(visit(ctx.selectionList()))); } else { - nested.append(QueryTokens.token(primaryFromAlias)); + // with CTE primary alias erors with hibernate + nested.append(containsCTE ? QueryTokens.token("*") : QueryTokens.token(primaryFromAlias)); +// nested.append(QueryTokens.token(primaryFromAlias)); } } else { builder.append(QueryTokens.token(countProjection)); diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/UserRepositoryFinderTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/UserRepositoryFinderTests.java index 09281ed623..34bc59c4d0 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/UserRepositoryFinderTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/UserRepositoryFinderTests.java @@ -47,6 +47,7 @@ import org.springframework.data.jpa.domain.sample.User; import org.springframework.data.jpa.provider.PersistenceProvider; import org.springframework.data.jpa.repository.sample.RoleRepository; +import org.springframework.data.jpa.repository.sample.UserExcerptDto; import org.springframework.data.jpa.repository.sample.UserRepository; import org.springframework.data.jpa.repository.sample.UserRepository.IdOnly; import org.springframework.data.jpa.repository.sample.UserRepository.NameOnly; @@ -433,6 +434,14 @@ void dtoMultiselectProjectionShouldApplyConstructorExpressionRewriting() { .contains("Dave", "Carter", "Oliver August"); } + @Test // GH-3726 + void xxx() { + + Page dtos = userRepository.findWithCTE(PageRequest.of(0, 1)); + System.out.println("dtos: " + dtos); + + } + @Test // GH-3076 void dynamicDtoProjection() { diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/HqlQueryTransformerTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/HqlQueryTransformerTests.java index 2ee28f804a..026aaedd27 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/HqlQueryTransformerTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/HqlQueryTransformerTests.java @@ -539,7 +539,7 @@ WITH maxId AS(select max(sr.snapshot.id) snapshotId from SnapshotReference sr """); assertThat(countQuery).startsWith("WITH maxId AS (select max(sr.snapshot.id) snapshotId from SnapshotReference sr") - .endsWith("select count(m) from maxId m join SnapshotReference sr on sr.snapshot.id = m.snapshotId"); + .endsWith("select count(*) from maxId m join SnapshotReference sr on sr.snapshot.id = m.snapshotId"); } @Test // GH-3504 diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/sample/UserExcerptDto.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/sample/UserExcerptDto.java new file mode 100644 index 0000000000..5e615573a2 --- /dev/null +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/sample/UserExcerptDto.java @@ -0,0 +1,47 @@ +/* + * Copyright 2025 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.data.jpa.repository.sample; + +/** + * @author Christoph Strobl + * @since 2025/01 + */ +public class UserExcerptDto { + + String firstname; + String lastname; + + public UserExcerptDto(String firstname, String lastname) { + this.firstname = firstname; + this.lastname = lastname; + } + + public String getFirstname() { + return firstname; + } + + public void setFirstname(String firstname) { + this.firstname = firstname; + } + + public String getLastname() { + return lastname; + } + + public void setLastname(String lastname) { + this.lastname = lastname; + } +} diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/sample/UserRepository.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/sample/UserRepository.java index 1676f886a5..e5b30d0de6 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/sample/UserRepository.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/sample/UserRepository.java @@ -732,6 +732,25 @@ List findAllAndSortByFunctionResultNamedParameter(@Param("namedParameter @Query("select u.firstname, u.lastname from User u") List findMultiselectRecordProjection(); + /* + WITH entities AS ( + SELECT + e.id as id, + e.number as number + FROM TestEntity e + ) + SELECT new com.example.demo.Result('X', c.id, c.number) + FROM entities c + """) + */ + + @Query(""" + WITH cte_select AS (select u.firstname as firstname, u.lastname as lastname from User u) + SELECT new org.springframework.data.jpa.repository.sample.UserExcerptDto(c.firstname, c.lastname) + FROM cte_select c +""") + Page findWithCTE(Pageable page); + @UserRoleCountProjectingQuery List dtoProjectionEntityAndAggregatedValue();