diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/NativeJpaQuery.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/NativeJpaQuery.java index 61c4c6ef1..c664af9f1 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/NativeJpaQuery.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/NativeJpaQuery.java @@ -93,8 +93,15 @@ final class NativeJpaQuery extends AbstractStringBasedJpaQuery { return result; } - return returnedType.isProjecting() && !getMetamodel().isJpaManaged(returnedType.getReturnedType()) // - ? Tuple.class - : result; + if (returnedType.isProjecting()) { + + if (returnedType.getReturnedType().isInterface()) { + return Tuple.class; + } + + return returnedType.getReturnedType(); + } + + return result; } } diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/UserRepositoryTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/UserRepositoryTests.java index 225336983..caf40e1d9 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/UserRepositoryTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/UserRepositoryTests.java @@ -67,6 +67,7 @@ import org.springframework.data.jpa.domain.sample.QUser; import org.springframework.data.jpa.domain.sample.Role; import org.springframework.data.jpa.domain.sample.SpecialUser; import org.springframework.data.jpa.domain.sample.User; +import org.springframework.data.jpa.repository.sample.NameOnlyRecord; import org.springframework.data.jpa.repository.sample.SampleEvaluationContextExtension.SampleSecurityContextHolder; import org.springframework.data.jpa.repository.sample.UserRepository; import org.springframework.data.jpa.repository.sample.UserRepository.NameOnly; @@ -2979,6 +2980,19 @@ class UserRepositoryTests { assertThat(result.getLastname()).isEqualTo(user.getLastname()); } + @Test // GH-2757 + void supportsRecordsWithNativeQueries() { + + flushTestUsers(); + + User user = repository.findAll().get(0); + + NameOnlyRecord result = repository.findRecordProjectionByNativeQuery(user.getId()); + + assertThat(result.firstname()).isEqualTo(user.getFirstname()); + assertThat(result.lastname()).isEqualTo(user.getLastname()); + } + @Test // DATAJPA-1248 void supportsProjectionsWithNativeQueriesAndCamelCaseProperty() { diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/sample/NameOnlyRecord.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/sample/NameOnlyRecord.java new file mode 100644 index 000000000..d8dd5fa1b --- /dev/null +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/sample/NameOnlyRecord.java @@ -0,0 +1,20 @@ +/* + * Copyright 2018-2024 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.repository.sample; + +public record NameOnlyRecord(String firstname, String 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 e8eb6ae75..76896ece7 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 @@ -561,6 +561,9 @@ public interface UserRepository extends JpaRepository, JpaSpecifi @Query(value = "SELECT firstname, lastname FROM SD_User WHERE id = ?1", nativeQuery = true) NameOnly findByNativeQuery(Integer id); + @NativeQuery("SELECT firstname, lastname FROM SD_User WHERE id = ?1") + NameOnlyRecord findRecordProjectionByNativeQuery(Integer id); + // GH-3155 @NativeQuery(value = "SELECT emailaddress, secondary_email_address FROM SD_User WHERE id = ?1", sqlResultSetMapping = "emailDto")