diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/AbstractStringBasedJpaQuery.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/AbstractStringBasedJpaQuery.java index f447716c1..f71e72d1e 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/AbstractStringBasedJpaQuery.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/AbstractStringBasedJpaQuery.java @@ -145,7 +145,7 @@ abstract class AbstractStringBasedJpaQuery extends AbstractJpaQuery { ReturnedType getReturnedType(ResultProcessor processor) { ReturnedType returnedType = processor.getReturnedType(); - Class returnedJavaType = processor.getReturnedType().getReturnedType(); + Class returnedJavaType = returnedType.getReturnedType(); if (!returnedType.isProjecting() || returnedJavaType.isInterface() || query.isNativeQuery()) { return returnedType; @@ -157,7 +157,8 @@ abstract class AbstractStringBasedJpaQuery extends AbstractJpaQuery { return returnedType; } - if ((known != null && !known) || returnedJavaType.isArray() || getMetamodel().isJpaManaged(returnedJavaType)) { + if ((known != null && !known) || returnedJavaType.isArray() || getMetamodel().isJpaManaged(returnedJavaType) + || !returnedType.needsCustomConstruction()) { if (known == null) { knownProjections.put(returnedJavaType, false); } diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/Country.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/Country.java new file mode 100644 index 000000000..e02b800bd --- /dev/null +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/Country.java @@ -0,0 +1,41 @@ +/* + * 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 + * + * 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, + * 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.domain.sample; + +/** + * @author Mark Paluch + */ +public class Country { + + private final String code; + + // workaround to avoid DTO projections as needsCustomConstruction is false. + private Country(Country other) { + this.code = other.code; + } + + private Country(String code) { + this.code = code; + } + + public static Country of(String code) { + return new Country(code); + } + + public String getCode() { + return code; + } +} diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/Customer.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/Customer.java index f8442a9ae..d1c3d3e0f 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/Customer.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/Customer.java @@ -25,7 +25,7 @@ import jakarta.persistence.Id; @Entity public class Customer { - @Id Long id; + @Id Long id; - String name; + String name; } diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/RepositoryWithCompositeKeyTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/RepositoryWithCompositeKeyTests.java index 20613cc1d..99665ecbf 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/RepositoryWithCompositeKeyTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/RepositoryWithCompositeKeyTests.java @@ -15,7 +15,7 @@ */ package org.springframework.data.jpa.repository; -import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.*; import jakarta.persistence.EntityManager; @@ -25,6 +25,7 @@ import java.util.List; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; + import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.domain.Page; import org.springframework.data.domain.PageRequest; @@ -115,6 +116,24 @@ class RepositoryWithCompositeKeyTests { assertThat(persistedEmp.getDepartment().getName()).isEqualTo(dep.getName()); } + @Test // GH-3929 + void shouldReturnIdentifiers() { + + EmbeddedIdExampleDepartment dep = new EmbeddedIdExampleDepartment(); + dep.setName("TestDepartment"); + dep.setDepartmentId(-1L); + + EmbeddedIdExampleEmployee emp = new EmbeddedIdExampleEmployee(); + emp.setDepartment(dep); + emp.setEmployeePk(new EmbeddedIdExampleEmployeePK(1L, 2L)); + + emp = employeeRepositoryWithEmbeddedId.save(emp); + + List identifiers = employeeRepositoryWithEmbeddedId.findIdentifiers(); + + assertThat(identifiers).hasSize(1).contains(emp.getEmployeePk()); + } + @Test // DATAJPA-472, DATAJPA-912 void shouldSupportFindAllWithPageableAndEntityWithIdClass() { diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/SimpleJpaQueryUnitTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/SimpleJpaQueryUnitTests.java index e2934c84d..45c247040 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/SimpleJpaQueryUnitTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/SimpleJpaQueryUnitTests.java @@ -45,6 +45,7 @@ import org.springframework.data.domain.Page; import org.springframework.data.domain.PageRequest; import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Sort; +import org.springframework.data.jpa.domain.sample.Country; import org.springframework.data.jpa.domain.sample.User; import org.springframework.data.jpa.provider.QueryExtractor; import org.springframework.data.jpa.repository.NativeQuery; @@ -325,6 +326,17 @@ class SimpleJpaQueryUnitTests { assertThatIllegalArgumentException().isThrownBy(() -> createJpaQuery(illegalMethod)); } + @Test // GH-3929 + void doesNotRewriteQueryForDtoWithMultipleConstructors() throws Exception { + + AbstractStringBasedJpaQuery jpaQuery = (AbstractStringBasedJpaQuery) createJpaQuery( + SampleRepository.class.getMethod("justCountries")); + + String queryString = createQuery(jpaQuery); + + assertThat(queryString).startsWith("select u.country from User u"); + } + @Test // DATAJPA-1163 void resolvesExpressionInCountQuery() throws Exception { @@ -408,6 +420,9 @@ class SimpleJpaQueryUnitTests { @Query("select r.name from User u LEFT JOIN FETCH u.roles r") Collection projectWithJoinPaths(); + @Query("select u.country from User u") + Collection justCountries(); + @Query(value = "select u from #{#entityName} u", countQuery = "select count(u.id) from #{#entityName} u") List findAllWithExpressionInCountQuery(Pageable pageable); diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/sample/EmployeeRepositoryWithEmbeddedId.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/sample/EmployeeRepositoryWithEmbeddedId.java index ea1bc60f5..7e8dce12b 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/sample/EmployeeRepositoryWithEmbeddedId.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/sample/EmployeeRepositoryWithEmbeddedId.java @@ -21,6 +21,7 @@ import org.springframework.context.annotation.Lazy; import org.springframework.data.jpa.domain.sample.EmbeddedIdExampleEmployee; import org.springframework.data.jpa.domain.sample.EmbeddedIdExampleEmployeePK; import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Query; import org.springframework.data.querydsl.QuerydslPredicateExecutor; import com.querydsl.core.types.OrderSpecifier; @@ -40,6 +41,9 @@ public interface EmployeeRepositoryWithEmbeddedId @Override List findAll(Predicate predicate, OrderSpecifier... orders); + @Query("select e.employeePk from EmbeddedIdExampleEmployee e") + List findIdentifiers(); + // DATAJPA-920 boolean existsByName(String name); }