From d151c455543243b8cd14b49a02f4148a30b2f92a Mon Sep 17 00:00:00 2001 From: Christoph Strobl Date: Tue, 7 Jan 2025 10:10:49 +0100 Subject: [PATCH 1/4] Upgrade to Hibernate 6.6.4.Final. Closes: #3710 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 1847f4d2c4..652224120f 100755 --- a/pom.xml +++ b/pom.xml @@ -30,7 +30,7 @@ 4.13.0 4.0.4 4.0.5-SNAPSHOT - 6.6.2.Final + 6.6.4.Final 6.2.31.Final 6.6.3-SNAPSHOT 7.0.0.Beta1 From f6d7eda546781ce716208295baacb575dcb1459b Mon Sep 17 00:00:00 2001 From: Christoph Strobl Date: Tue, 7 Jan 2025 10:27:37 +0100 Subject: [PATCH 2/4] Prepare issue branch. --- pom.xml | 2 +- spring-data-envers/pom.xml | 4 ++-- spring-data-jpa-distribution/pom.xml | 2 +- spring-data-jpa/pom.xml | 4 ++-- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/pom.xml b/pom.xml index 652224120f..bc0025bd35 100755 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ org.springframework.data spring-data-jpa-parent - 3.5.0-SNAPSHOT + 3.5.x-GH-3726-SNAPSHOT pom Spring Data JPA Parent diff --git a/spring-data-envers/pom.xml b/spring-data-envers/pom.xml index 5dcbbb69fd..a3a40ac100 100755 --- a/spring-data-envers/pom.xml +++ b/spring-data-envers/pom.xml @@ -5,12 +5,12 @@ org.springframework.data spring-data-envers - 3.5.0-SNAPSHOT + 3.5.x-GH-3726-SNAPSHOT org.springframework.data spring-data-jpa-parent - 3.5.0-SNAPSHOT + 3.5.x-GH-3726-SNAPSHOT ../pom.xml diff --git a/spring-data-jpa-distribution/pom.xml b/spring-data-jpa-distribution/pom.xml index 38a234cb71..ebf928fbaf 100644 --- a/spring-data-jpa-distribution/pom.xml +++ b/spring-data-jpa-distribution/pom.xml @@ -14,7 +14,7 @@ org.springframework.data spring-data-jpa-parent - 3.5.0-SNAPSHOT + 3.5.x-GH-3726-SNAPSHOT ../pom.xml diff --git a/spring-data-jpa/pom.xml b/spring-data-jpa/pom.xml index c6d9301c02..f3823eba35 100644 --- a/spring-data-jpa/pom.xml +++ b/spring-data-jpa/pom.xml @@ -6,7 +6,7 @@ org.springframework.data spring-data-jpa - 3.5.0-SNAPSHOT + 3.5.x-GH-3726-SNAPSHOT Spring Data JPA Spring Data module for JPA repositories. @@ -15,7 +15,7 @@ org.springframework.data spring-data-jpa-parent - 3.5.0-SNAPSHOT + 3.5.x-GH-3726-SNAPSHOT ../pom.xml From 7d35720d80a4aa81c1c3df67ce31188630d4deac Mon Sep 17 00:00:00 2001 From: Christoph Strobl Date: Tue, 7 Jan 2025 13:35:01 +0100 Subject: [PATCH 3/4] hacking - first try --- .../query/HqlCountQueryTransformer.java | 13 ++++- .../repository/UserRepositoryFinderTests.java | 9 ++++ .../query/HqlQueryTransformerTests.java | 2 +- .../jpa/repository/sample/UserExcerptDto.java | 47 +++++++++++++++++++ .../jpa/repository/sample/UserRepository.java | 19 ++++++++ 5 files changed, 88 insertions(+), 2 deletions(-) create mode 100644 spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/sample/UserExcerptDto.java 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(); From b80794fc71dd60d47f85e22c28b51144853610e9 Mon Sep 17 00:00:00 2001 From: Christoph Strobl Date: Tue, 7 Jan 2025 14:45:46 +0100 Subject: [PATCH 4/4] make sure to disable validation while tests are running --- spring-data-jpa/pom.xml | 6 +++ .../jpa/repository/query/SimpleJpaQuery.java | 5 +++ ...raphRepositoryMethodsIntegrationTests.java | 2 + ...eLinkParentRepositoryIntegrationTests.java | 2 + ...itoryWithCompositeKeyIntegrationTests.java | 2 + .../EclipseLinkUserRepositoryFinderTests.java | 7 ++++ .../repository/UserRepositoryFinderTests.java | 7 ++-- .../query/EclipseLinkJpa21UtilsTests.java | 2 + ...aAnnotatedQueryMethodIntegrationTests.java | 2 + .../jpa/repository/sample/UserExcerptDto.java | 41 ++++++++++--------- 10 files changed, 52 insertions(+), 24 deletions(-) diff --git a/spring-data-jpa/pom.xml b/spring-data-jpa/pom.xml index f3823eba35..fba223beb2 100644 --- a/spring-data-jpa/pom.xml +++ b/spring-data-jpa/pom.xml @@ -98,6 +98,12 @@ junit-platform-launcher test + + org.junit-pioneer + junit-pioneer + 2.3.0 + test + org.hsqldb diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/SimpleJpaQuery.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/SimpleJpaQuery.java index 16fa3c30e0..1fc5c31663 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/SimpleJpaQuery.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/SimpleJpaQuery.java @@ -18,6 +18,7 @@ import jakarta.persistence.EntityManager; import jakarta.persistence.Query; +import org.springframework.core.SpringProperties; import org.springframework.data.jpa.repository.QueryRewriter; import org.springframework.data.repository.query.RepositoryQuery; import org.springframework.data.repository.query.ValueExpressionDelegate; @@ -84,6 +85,10 @@ private void validateQuery(String query, String errorMessage, Object... argument return; } + if(SpringProperties.getFlag("spring.jpa.query.validation.disbaled")) { + return; + } + EntityManager validatingEm = null; try { diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/EclipseLinkEntityGraphRepositoryMethodsIntegrationTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/EclipseLinkEntityGraphRepositoryMethodsIntegrationTests.java index e0d21dc716..17d6e7771f 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/EclipseLinkEntityGraphRepositoryMethodsIntegrationTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/EclipseLinkEntityGraphRepositoryMethodsIntegrationTests.java @@ -17,6 +17,7 @@ import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; +import org.junitpioneer.jupiter.SetSystemProperty; import org.springframework.test.context.ContextConfiguration; /** @@ -24,6 +25,7 @@ * @author Christoph Strobl */ @ContextConfiguration("classpath:eclipselink.xml") +@SetSystemProperty(key = "spring.jpa.query.validation.disbaled", value = "true") class EclipseLinkEntityGraphRepositoryMethodsIntegrationTests extends EntityGraphRepositoryMethodsIntegrationTests { diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/EclipseLinkParentRepositoryIntegrationTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/EclipseLinkParentRepositoryIntegrationTests.java index 06d1575fd5..52a8dad60f 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/EclipseLinkParentRepositoryIntegrationTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/EclipseLinkParentRepositoryIntegrationTests.java @@ -15,9 +15,11 @@ */ package org.springframework.data.jpa.repository; +import org.junitpioneer.jupiter.SetSystemProperty; import org.springframework.test.context.ContextConfiguration; @ContextConfiguration("classpath:eclipselink.xml") +@SetSystemProperty(key = "spring.jpa.query.validation.disbaled", value = "true") class EclipseLinkParentRepositoryIntegrationTests extends ParentRepositoryIntegrationTests { } diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/EclipseLinkRepositoryWithCompositeKeyIntegrationTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/EclipseLinkRepositoryWithCompositeKeyIntegrationTests.java index 82ca0c59a9..eba90ffcfb 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/EclipseLinkRepositoryWithCompositeKeyIntegrationTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/EclipseLinkRepositoryWithCompositeKeyIntegrationTests.java @@ -15,6 +15,7 @@ */ package org.springframework.data.jpa.repository; +import org.junitpioneer.jupiter.SetSystemProperty; import org.springframework.context.annotation.ImportResource; import org.springframework.test.context.ContextConfiguration; @@ -24,6 +25,7 @@ * @author Mark Paluch */ @ContextConfiguration +@SetSystemProperty(key = "spring.jpa.query.validation.disbaled", value = "true") class EclipseLinkRepositoryWithCompositeKeyIntegrationTests extends RepositoryWithIdClassKeyTests { @ImportResource({ "classpath:infrastructure.xml", "classpath:eclipselink.xml" }) diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/EclipseLinkUserRepositoryFinderTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/EclipseLinkUserRepositoryFinderTests.java index 18e0570de8..07953bf97f 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/EclipseLinkUserRepositoryFinderTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/EclipseLinkUserRepositoryFinderTests.java @@ -16,6 +16,7 @@ package org.springframework.data.jpa.repository; import org.junit.jupiter.api.Disabled; +import org.junitpioneer.jupiter.SetSystemProperty; import org.springframework.test.context.ContextConfiguration; /** @@ -26,6 +27,7 @@ * @author Greg Turnquist */ @ContextConfiguration("classpath:eclipselink-h2.xml") +@SetSystemProperty(key = "spring.jpa.query.validation.disbaled", value = "true") class EclipseLinkUserRepositoryFinderTests extends UserRepositoryFinderTests { @Disabled @@ -40,4 +42,9 @@ void executesInKeywordForPageCorrectly() {} @Override void rawMapProjectionWithEntityAndAggregatedValue() {} + @Disabled + @Override + void testQueryWithCTE() { + super.testQueryWithCTE(); + } } 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 34bc59c4d0..ecc14cd3d1 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 @@ -435,11 +435,10 @@ void dtoMultiselectProjectionShouldApplyConstructorExpressionRewriting() { } @Test // GH-3726 - void xxx() { - - Page dtos = userRepository.findWithCTE(PageRequest.of(0, 1)); - System.out.println("dtos: " + dtos); + void testQueryWithCTE() { + Page result = userRepository.findWithCTE(PageRequest.of(0, 1)); + assertThat(result.getTotalElements()).isEqualTo(3); } @Test // GH-3076 diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/EclipseLinkJpa21UtilsTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/EclipseLinkJpa21UtilsTests.java index bd97fbb600..d87bdf8034 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/EclipseLinkJpa21UtilsTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/EclipseLinkJpa21UtilsTests.java @@ -15,10 +15,12 @@ */ package org.springframework.data.jpa.repository.query; +import org.junitpioneer.jupiter.SetSystemProperty; import org.springframework.test.context.ContextConfiguration; /** * @author Christoph Strobl */ @ContextConfiguration("classpath:eclipselink.xml") +@SetSystemProperty(key = "spring.jpa.query.validation.disbaled", value = "true") class EclipseLinkJpa21UtilsTests extends Jpa21UtilsTests {} diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/EclipseLinkMetaAnnotatedQueryMethodIntegrationTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/EclipseLinkMetaAnnotatedQueryMethodIntegrationTests.java index df7b27f9d9..8427549b20 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/EclipseLinkMetaAnnotatedQueryMethodIntegrationTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/EclipseLinkMetaAnnotatedQueryMethodIntegrationTests.java @@ -33,6 +33,7 @@ import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; +import org.junitpioneer.jupiter.SetSystemProperty; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @@ -69,6 +70,7 @@ @ExtendWith(SpringExtension.class) @ContextConfiguration @Transactional +@SetSystemProperty(key = "spring.jpa.query.validation.disbaled", value = "true") class EclipseLinkMetaAnnotatedQueryMethodIntegrationTests { @Autowired RoleRepositoryWithMeta repository; 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 index 5e615573a2..af9e522267 100644 --- 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 @@ -5,7 +5,7 @@ * 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 + * https://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, @@ -16,32 +16,33 @@ package org.springframework.data.jpa.repository.sample; /** + * Hibernate is still a bit picky on records so let's use a class, just in case. + * * @author Christoph Strobl - * @since 2025/01 */ public class UserExcerptDto { - String firstname; - String lastname; + private String firstname; + private String lastname; - public UserExcerptDto(String firstname, String lastname) { - this.firstname = firstname; - this.lastname = lastname; - } + public UserExcerptDto(String firstname, String lastname) { + this.firstname = firstname; + this.lastname = lastname; + } - public String getFirstname() { - return firstname; - } + public String getFirstname() { + return firstname; + } - public void setFirstname(String firstname) { - this.firstname = firstname; - } + public void setFirstname(String firstname) { + this.firstname = firstname; + } - public String getLastname() { - return lastname; - } + public String getLastname() { + return lastname; + } - public void setLastname(String lastname) { - this.lastname = lastname; - } + public void setLastname(String lastname) { + this.lastname = lastname; + } }