Skip to content

Commit f78e87f

Browse files
infoSharemp911de
authored andcommitted
Consider actual query method return type in AOT-generated repository implementations.
We now consider the actual query method return type in AOT generated fragments. Ensure AOT-generated code uses correct entity class in query methods returning subclass types. Closes #5178 Original pull request: #5179 Signed-off-by: Tomasz Forys <tomito14@interia.pl>
1 parent 0f3151f commit f78e87f

File tree

2 files changed

+48
-9
lines changed

2 files changed

+48
-9
lines changed

spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/aot/QueryBlocks.java

Lines changed: 13 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
import org.springframework.data.mongodb.core.query.DiskUse;
3131
import org.springframework.data.mongodb.repository.Hint;
3232
import org.springframework.data.mongodb.repository.Meta;
33+
import org.springframework.data.mongodb.repository.query.MongoEntityMetadata;
3334
import org.springframework.data.mongodb.repository.query.MongoQueryExecution.PagedExecution;
3435
import org.springframework.data.mongodb.repository.query.MongoQueryExecution.SlicedExecution;
3536
import org.springframework.data.mongodb.repository.query.MongoQueryMethod;
@@ -44,6 +45,7 @@
4445

4546
/**
4647
* @author Christoph Strobl
48+
* @author Tomasz Forys
4749
* @since 5.0
4850
*/
4951
class QueryBlocks {
@@ -71,27 +73,29 @@ CodeBlock build() {
7173

7274
MethodReturn methodReturn = context.getMethodReturn();
7375
String mongoOpsRef = context.fieldNameOf(MongoOperations.class);
76+
MongoEntityMetadata<?> entityMetadata = queryMethod.getEntityInformation();
7477

7578
Builder builder = CodeBlock.builder();
7679

7780
boolean isProjecting = context.getReturnedType().isProjecting();
78-
Class<?> domainType = context.getRepositoryInformation().getDomainType();
79-
Object actualReturnType = queryMethod.getParameters().hasDynamicProjection() || isProjecting
81+
boolean hasDynamicProjection = queryMethod.getParameters().hasDynamicProjection();
82+
Class<?> queryType = entityMetadata.getCollectionEntity().getType();
83+
Class<?> entityType = entityMetadata.getJavaType();
84+
Object actualReturnType = hasDynamicProjection || isProjecting
8085
? methodReturn.getActualTypeName()
81-
: domainType;
86+
: entityType;
8287

8388
builder.add("\n");
8489

85-
if (queryMethod.getParameters().hasDynamicProjection()) {
90+
if (hasDynamicProjection) {
8691
builder.addStatement("$T<$T> $L = $L.query($T.class).as($L)", FindWithQuery.class, actualReturnType,
87-
context.localVariable("finder"), mongoOpsRef, domainType, context.getDynamicProjectionParameterName());
88-
} else if (isProjecting) {
92+
context.localVariable("finder"), mongoOpsRef, queryType, context.getDynamicProjectionParameterName());
93+
} else if (isProjecting || !queryType.equals(entityType)) {
8994
builder.addStatement("$T<$T> $L = $L.query($T.class).as($T.class)", FindWithQuery.class, actualReturnType,
90-
context.localVariable("finder"), mongoOpsRef, domainType, actualReturnType);
95+
context.localVariable("finder"), mongoOpsRef, queryType, actualReturnType);
9196
} else {
92-
9397
builder.addStatement("$T<$T> $L = $L.query($T.class)", FindWithQuery.class, actualReturnType,
94-
context.localVariable("finder"), mongoOpsRef, domainType);
98+
context.localVariable("finder"), mongoOpsRef, queryType);
9599
}
96100

97101
String terminatingMethod;

spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/aot/QueryMethodContributionUnitTests.java

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
import java.util.Arrays;
2525
import java.util.Collection;
2626
import java.util.List;
27+
import java.util.Optional;
2728
import java.util.regex.Pattern;
2829

2930
import org.junit.jupiter.api.Assertions;
@@ -47,6 +48,7 @@
4748
import org.springframework.data.mongodb.core.geo.GeoJsonPolygon;
4849
import org.springframework.data.mongodb.core.geo.Sphere;
4950
import org.springframework.data.mongodb.repository.Hint;
51+
import org.springframework.data.mongodb.repository.Query;
5052
import org.springframework.data.mongodb.repository.ReadPreference;
5153
import org.springframework.data.mongodb.repository.VectorSearch;
5254
import org.springframework.data.repository.Repository;
@@ -61,6 +63,7 @@
6163
*
6264
* @author Christoph Strobl
6365
* @author Mark Paluch
66+
* @author Tomasz Forys
6467
*/
6568
class QueryMethodContributionUnitTests {
6669

@@ -406,6 +409,30 @@ void rendersStreamableReturnTypeForAggregation() throws NoSuchMethodException {
406409
.containsSubsequence("return", "Streamable.of(", "getMappedResults())");
407410
}
408411

412+
@Test // GH-5178
413+
void rendersAnnotatedQueryUsingSubtypeAsCollectionAndResultType() throws NoSuchMethodException {
414+
415+
MethodSpec methodSpec = codeOf(UserRepoWithMeta.class, "findSpecialUserByLastname", String.class);
416+
417+
assertThat(methodSpec.toString()) //
418+
.containsSubsequence("FindWithQuery<", ".SpecialUser> finder =")
419+
.containsSubsequence(".query(",".SpecialUser.class)")
420+
.doesNotContain(".query(example.aot.User.class)")
421+
.contains("return finder.matching(filterQuery).all()");
422+
}
423+
424+
@Test // GH-5178
425+
void rendersOptionalAnnotatedQueryUsingSubtypeAsCollectionAndResultType() throws NoSuchMethodException {
426+
427+
MethodSpec methodSpec = codeOf(UserRepoWithMeta.class, "findOptionalSubtypeByUsername", String.class);
428+
429+
assertThat(methodSpec.toString()) //
430+
.containsSubsequence("FindWithQuery<", ".SpecialUser> finder =")
431+
.containsSubsequence(".query(",".SpecialUser.class)")
432+
.doesNotContain(".query(example.aot.User.class)")
433+
.contains("return finder.matching(filterQuery).one()");
434+
}
435+
409436
private static MethodSpec codeOf(Class<?> repository, String methodName, Class<?>... args)
410437
throws NoSuchMethodException {
411438

@@ -445,6 +472,8 @@ static class TestQueryMethodGenerationContext extends AotQueryMethodGenerationCo
445472
}
446473
}
447474

475+
static class SpecialUser extends User {}
476+
448477
interface UserRepoWithMeta extends Repository<User, String> {
449478

450479
@Hint("fn-idx")
@@ -460,5 +489,11 @@ interface UserRepoWithMeta extends Repository<User, String> {
460489
@VectorSearch(indexName = "embedding.vector_cos", limit = "#{5+5}")
461490
SearchResults<User> searchWithLimitAsExpressionByLastnameAndEmbeddingWithinOrderByFirstname(String lastname,
462491
Vector vector, Range<Similarity> distance);
492+
493+
@Query("{ 'lastname' : { '$regex' : '^?0' } }")
494+
List<SpecialUser> findSpecialUserByLastname(String lastname);
495+
496+
@Query("{ 'username' : ?0 }")
497+
Optional<SpecialUser> findOptionalSubtypeByUsername(String username);
463498
}
464499
}

0 commit comments

Comments
 (0)