Skip to content

Commit b0f8c51

Browse files
committed
Fix Native Query projections mapping to DTOs.
We now provide the target type to createNativeQuery(…) if we detect that a native query result type is a projection and not an interface. Closes #2757
1 parent 229db64 commit b0f8c51

File tree

4 files changed

+47
-3
lines changed

4 files changed

+47
-3
lines changed

spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/NativeJpaQuery.java

+10-3
Original file line numberDiff line numberDiff line change
@@ -93,8 +93,15 @@ private Class<?> getTypeToQueryFor(ReturnedType returnedType) {
9393
return result;
9494
}
9595

96-
return returnedType.isProjecting() && !getMetamodel().isJpaManaged(returnedType.getReturnedType()) //
97-
? Tuple.class
98-
: result;
96+
if (returnedType.isProjecting()) {
97+
98+
if (returnedType.getReturnedType().isInterface()) {
99+
return Tuple.class;
100+
}
101+
102+
return returnedType.getReturnedType();
103+
}
104+
105+
return result;
99106
}
100107
}

spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/UserRepositoryTests.java

+14
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@
6767
import org.springframework.data.jpa.domain.sample.Role;
6868
import org.springframework.data.jpa.domain.sample.SpecialUser;
6969
import org.springframework.data.jpa.domain.sample.User;
70+
import org.springframework.data.jpa.repository.sample.NameOnlyRecord;
7071
import org.springframework.data.jpa.repository.sample.SampleEvaluationContextExtension.SampleSecurityContextHolder;
7172
import org.springframework.data.jpa.repository.sample.UserRepository;
7273
import org.springframework.data.jpa.repository.sample.UserRepository.NameOnly;
@@ -2979,6 +2980,19 @@ void supportsInterfaceProjectionsWithNativeQueries() {
29792980
assertThat(result.getLastname()).isEqualTo(user.getLastname());
29802981
}
29812982

2983+
@Test // GH-2757
2984+
void supportsRecordsWithNativeQueries() {
2985+
2986+
flushTestUsers();
2987+
2988+
User user = repository.findAll().get(0);
2989+
2990+
NameOnlyRecord result = repository.findRecordProjectionByNativeQuery(user.getId());
2991+
2992+
assertThat(result.firstname()).isEqualTo(user.getFirstname());
2993+
assertThat(result.lastname()).isEqualTo(user.getLastname());
2994+
}
2995+
29822996
@Test // DATAJPA-1248
29832997
void supportsProjectionsWithNativeQueriesAndCamelCaseProperty() {
29842998

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
/*
2+
* Copyright 2018-2024 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package org.springframework.data.jpa.repository.sample;
17+
18+
public record NameOnlyRecord(String firstname, String lastname) {
19+
20+
}

spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/sample/UserRepository.java

+3
Original file line numberDiff line numberDiff line change
@@ -558,6 +558,9 @@ List<User> findUsersByFirstnameForSpELExpressionWithParameterIndexOnlyWithEntity
558558
@Query(value = "SELECT firstname, lastname FROM SD_User WHERE id = ?1", nativeQuery = true)
559559
NameOnly findByNativeQuery(Integer id);
560560

561+
@NativeQuery("SELECT firstname, lastname FROM SD_User WHERE id = ?1")
562+
NameOnlyRecord findRecordProjectionByNativeQuery(Integer id);
563+
561564
// GH-3155
562565
@NativeQuery(value = "SELECT emailaddress, secondary_email_address FROM SD_User WHERE id = ?1",
563566
sqlResultSetMapping = "emailDto")

0 commit comments

Comments
 (0)