From e4e79c4c94477ed58c9f447b05ec2abbcd6a633d Mon Sep 17 00:00:00 2001 From: Christoph Strobl Date: Mon, 15 Jan 2024 08:19:16 +0100 Subject: [PATCH 1/5] Prepare issue branch. --- pom.xml | 2 +- spring-data-mongodb-benchmarks/pom.xml | 2 +- spring-data-mongodb-distribution/pom.xml | 2 +- spring-data-mongodb/pom.xml | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/pom.xml b/pom.xml index 92c8f2562b..d282a42f4f 100644 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ org.springframework.data spring-data-mongodb-parent - 4.2.3-SNAPSHOT + 4.2.x-4609-SNAPSHOT pom Spring Data MongoDB diff --git a/spring-data-mongodb-benchmarks/pom.xml b/spring-data-mongodb-benchmarks/pom.xml index f0f146edd9..1e70a28eb0 100644 --- a/spring-data-mongodb-benchmarks/pom.xml +++ b/spring-data-mongodb-benchmarks/pom.xml @@ -7,7 +7,7 @@ org.springframework.data spring-data-mongodb-parent - 4.2.3-SNAPSHOT + 4.2.x-4609-SNAPSHOT ../pom.xml diff --git a/spring-data-mongodb-distribution/pom.xml b/spring-data-mongodb-distribution/pom.xml index 6876c90f9f..6c2f6ffb56 100644 --- a/spring-data-mongodb-distribution/pom.xml +++ b/spring-data-mongodb-distribution/pom.xml @@ -15,7 +15,7 @@ org.springframework.data spring-data-mongodb-parent - 4.2.3-SNAPSHOT + 4.2.x-4609-SNAPSHOT ../pom.xml diff --git a/spring-data-mongodb/pom.xml b/spring-data-mongodb/pom.xml index 4e070e04d4..c43dfc392d 100644 --- a/spring-data-mongodb/pom.xml +++ b/spring-data-mongodb/pom.xml @@ -13,7 +13,7 @@ org.springframework.data spring-data-mongodb-parent - 4.2.3-SNAPSHOT + 4.2.x-4609-SNAPSHOT ../pom.xml From 45bbae725700f432406b4b603192eab830660627 Mon Sep 17 00:00:00 2001 From: Christoph Strobl Date: Mon, 15 Jan 2024 08:32:41 +0100 Subject: [PATCH 2/5] add failing testcase --- .../data/mongodb/core/MongoTemplateTests.java | 24 +++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/MongoTemplateTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/MongoTemplateTests.java index 7bdf99ee28..88f2eafb62 100644 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/MongoTemplateTests.java +++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/MongoTemplateTests.java @@ -2554,6 +2554,30 @@ public void findAndReplaceShouldProjectReturnedObjectCorrectly() { assertThat(projection.getName()).isEqualTo("Walter"); } + @Test // GH-4609 + public void shouldReadNestedProjection() { + + MyPerson walter = new MyPerson("Walter"); + walter.address = new Address("some", "city"); + template.save(walter); + + PersonPWA result = template.query(MyPerson.class) + .as(PersonPWA.class) + .matching(where("id").is(walter.id)) + .firstValue(); + + System.out.println("result: " + result.getAddress().getCity()); + } + + interface PersonPWA { + String getName(); + AdressProjection getAddress(); + } + + interface AdressProjection { + String getCity(); + } + @Test // GH-4300 public void findAndReplaceShouldAllowNativeDomainTypesAndReturnAProjection() { From f5046edc3f45695f3fb73d55636af62214e06884 Mon Sep 17 00:00:00 2001 From: Christoph Strobl Date: Tue, 16 Jan 2024 11:20:29 +0100 Subject: [PATCH 3/5] use projection context for interface based proections --- .../mongodb/core/convert/MappingMongoConverter.java | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/MappingMongoConverter.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/MappingMongoConverter.java index 8d597d81d9..3c5cc0851c 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/MappingMongoConverter.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/MappingMongoConverter.java @@ -2436,6 +2436,18 @@ class ProjectingConversionContext extends DefaultConversionContext { this.returnedTypeDescriptor = projection; } + @Override + public ConversionContext forProperty(MongoPersistentProperty property) { + + if(returnedTypeDescriptor.isProjection() && returnedTypeDescriptor.getDomainType().equals(property.getTypeInformation())) { + if(returnedTypeDescriptor.getMappedType().getType().isInterface()) { + return this; + } + } + + return super.forProperty(property); + } + @Override public ConversionContext forProperty(String name) { From db02adc59cf18687bde8625b65f68c1ccc0be87d Mon Sep 17 00:00:00 2001 From: Christoph Strobl Date: Tue, 16 Jan 2024 11:56:11 +0100 Subject: [PATCH 4/5] Additional tests --- .../MappingMongoConverterUnitTests.java | 62 +++++++++++++++++++ 1 file changed, 62 insertions(+) diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/convert/MappingMongoConverterUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/convert/MappingMongoConverterUnitTests.java index 857ded9cf6..8a73aa2e9c 100644 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/convert/MappingMongoConverterUnitTests.java +++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/convert/MappingMongoConverterUnitTests.java @@ -2851,6 +2851,44 @@ void projectShouldReadNestedProjection() { assertThat(person.getAddresses()).extracting(AddressProjection::getStreet).hasSize(1).containsOnly("hwy"); } + @Test // GH-4609 + void projectShouldReadNestedInterfaceProjection() { + + org.bson.Document source = new org.bson.Document("foo", "spring").append("address", + new org.bson.Document("s", "data").append("city", "mongodb")); + + EntityProjectionIntrospector introspector = EntityProjectionIntrospector.create(converter.getProjectionFactory(), + EntityProjectionIntrospector.ProjectionPredicate.typeHierarchy() + .and((target, underlyingType) -> !converter.conversions.isSimpleType(target)), + mappingContext); + + EntityProjection projection = introspector.introspect(WithNestedInterfaceProjection.class, + Person.class); + WithNestedInterfaceProjection person = converter.project(projection, source); + + assertThat(person.getFirstname()).isEqualTo("spring"); + assertThat(person.getAddress().getStreet()).isEqualTo("data"); + } + + @Test // GH-4609 + void projectShouldReadNestedDtoProjection() { + + org.bson.Document source = new org.bson.Document("foo", "spring").append("address", + new org.bson.Document("s", "data").append("city", "mongodb")); + + EntityProjectionIntrospector introspector = EntityProjectionIntrospector.create(converter.getProjectionFactory(), + EntityProjectionIntrospector.ProjectionPredicate.typeHierarchy() + .and((target, underlyingType) -> !converter.conversions.isSimpleType(target)), + mappingContext); + + EntityProjection projection = introspector.introspect(WithNestedDtoProjection.class, + Person.class); + WithNestedDtoProjection person = converter.project(projection, source); + + assertThat(person.getFirstname()).isEqualTo("spring"); + assertThat(person.getAddress().getStreet()).isEqualTo("data"); + } + @Test // GH-2860 void projectShouldReadProjectionWithNestedEntity() { @@ -3206,6 +3244,7 @@ static class Person implements Contact { String lastname; Set
addresses; + Address address; Person() { @@ -3248,6 +3287,16 @@ interface WithNestedProjection { Set getAddresses(); } + interface WithNestedInterfaceProjection { + String getFirstname(); + AddressProjection getAddress(); + } + + interface WithNestedDtoProjection { + String getFirstname(); + AddressDto getAddress(); + } + interface ProjectionWithNestedEntity { Set
getAddresses(); @@ -3258,6 +3307,19 @@ interface AddressProjection { String getStreet(); } + class AddressDto { + + String street; + + public String getStreet() { + return street; + } + + public void setStreet(String street) { + this.street = street; + } + } + static class PersonDto { LocalDate birthDate; From 66444be7dad0caa583529ccf1d0d6bd83d31760d Mon Sep 17 00:00:00 2001 From: Christoph Strobl Date: Tue, 16 Jan 2024 13:36:03 +0100 Subject: [PATCH 5/5] Avoid duplicating conversions --- .../mongodb/core/convert/MappingMongoConverter.java | 13 +------------ .../data/mongodb/core/MongoTemplateTests.java | 4 ++-- 2 files changed, 3 insertions(+), 14 deletions(-) diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/MappingMongoConverter.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/MappingMongoConverter.java index 3c5cc0851c..b6cbd824da 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/MappingMongoConverter.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/MappingMongoConverter.java @@ -595,7 +595,6 @@ private void readProperties(ConversionContext context, MongoPersistentEntity } ConversionContext propertyContext = context.forProperty(prop); - MongoDbPropertyValueProvider valueProviderToUse = valueProvider.withContext(propertyContext); if (prop.isAssociation()) { @@ -623,7 +622,7 @@ private void readProperties(ConversionContext context, MongoPersistentEntity continue; } - accessor.setProperty(prop, valueProviderToUse.getPropertyValue(prop)); + accessor.setProperty(prop, valueProvider.getPropertyValue(prop)); } } @@ -2436,17 +2435,7 @@ class ProjectingConversionContext extends DefaultConversionContext { this.returnedTypeDescriptor = projection; } - @Override - public ConversionContext forProperty(MongoPersistentProperty property) { - - if(returnedTypeDescriptor.isProjection() && returnedTypeDescriptor.getDomainType().equals(property.getTypeInformation())) { - if(returnedTypeDescriptor.getMappedType().getType().isInterface()) { - return this; - } - } - return super.forProperty(property); - } @Override public ConversionContext forProperty(String name) { diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/MongoTemplateTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/MongoTemplateTests.java index 88f2eafb62..a1543aba86 100644 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/MongoTemplateTests.java +++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/MongoTemplateTests.java @@ -2558,7 +2558,7 @@ public void findAndReplaceShouldProjectReturnedObjectCorrectly() { public void shouldReadNestedProjection() { MyPerson walter = new MyPerson("Walter"); - walter.address = new Address("some", "city"); + walter.address = new Address("spring", "data"); template.save(walter); PersonPWA result = template.query(MyPerson.class) @@ -2566,7 +2566,7 @@ public void shouldReadNestedProjection() { .matching(where("id").is(walter.id)) .firstValue(); - System.out.println("result: " + result.getAddress().getCity()); + assertThat(result.getAddress().getCity()).isEqualTo("data"); } interface PersonPWA {