Skip to content

Commit 52886e1

Browse files
committed
Fix DTO projection instantiation.
We now correctly instantiate DTO projection classes by using the actual constructor argument type. Previously, we did not update the conversion context to fetch the correct type but used the type of the DTO projection class instead of the constructor argument. Closes #4120
1 parent d7ec16e commit 52886e1

File tree

2 files changed

+46
-10
lines changed

2 files changed

+46
-10
lines changed

Diff for: spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/MappingMongoConverter.java

+13-10
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@
3838
import org.bson.conversions.Bson;
3939
import org.bson.json.JsonReader;
4040
import org.bson.types.ObjectId;
41+
4142
import org.springframework.beans.BeansException;
4243
import org.springframework.beans.factory.BeanClassLoaderAware;
4344
import org.springframework.context.ApplicationContext;
@@ -415,8 +416,8 @@ public ConversionContext forProperty(String name) {
415416

416417
EntityProjection<?, ?> property = returnedTypeDescriptor.findProperty(name);
417418
if (property == null) {
418-
return new ConversionContext(sourceConverter, conversions, path, MappingMongoConverter.this::readDocument, collectionConverter,
419-
mapConverter, dbRefConverter, elementConverter);
419+
return new ConversionContext(sourceConverter, conversions, path, MappingMongoConverter.this::readDocument,
420+
collectionConverter, mapConverter, dbRefConverter, elementConverter);
420421
}
421422

422423
return new ProjectingConversionContext(sourceConverter, conversions, path, collectionConverter, mapConverter,
@@ -966,8 +967,8 @@ protected void writePropertyInternal(@Nullable Object obj, DocumentAccessor acce
966967
TypeInformation<?> type = prop.getTypeInformation();
967968

968969
if (conversions.getPropertyValueConversions().hasValueConverter(prop)) {
969-
accessor.put(prop,
970-
conversions.getPropertyValueConversions().getValueConverter(prop).write(obj, new MongoConversionContext(prop, this)));
970+
accessor.put(prop, conversions.getPropertyValueConversions().getValueConverter(prop).write(obj,
971+
new MongoConversionContext(prop, this)));
971972
return;
972973
}
973974

@@ -1302,9 +1303,8 @@ private void writeSimpleInternal(@Nullable Object value, Bson bson, MongoPersist
13021303
DocumentAccessor accessor = new DocumentAccessor(bson);
13031304

13041305
if (conversions.getPropertyValueConversions().hasValueConverter(property)) {
1305-
accessor.put(property,
1306-
conversions.getPropertyValueConversions().getValueConverter(property)
1307-
.write(value, new MongoConversionContext(property, this)));
1306+
accessor.put(property, conversions.getPropertyValueConversions().getValueConverter(property).write(value,
1307+
new MongoConversionContext(property, this)));
13081308
return;
13091309
}
13101310

@@ -1971,12 +1971,15 @@ public <T> T getPropertyValue(MongoPersistentProperty property) {
19711971
return null;
19721972
}
19731973

1974-
if (context.conversions.getPropertyValueConversions().hasValueConverter(property)) {
1975-
return (T) context.conversions.getPropertyValueConversions().getValueConverter(property).read(value,
1974+
CustomConversions conversions = context.conversions;
1975+
if (conversions.getPropertyValueConversions().hasValueConverter(property)) {
1976+
return (T) conversions.getPropertyValueConversions().getValueConverter(property).read(value,
19761977
new MongoConversionContext(property, context.sourceConverter));
19771978
}
19781979

1979-
return (T) context.convert(value, property.getTypeInformation());
1980+
ConversionContext contextToUse = context.forProperty(property.getName());
1981+
1982+
return (T) contextToUse.convert(value, property.getTypeInformation());
19801983
}
19811984

19821985
public MongoDbPropertyValueProvider withContext(ConversionContext context) {

Diff for: spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/convert/MappingMongoConverterUnitTests.java

+33
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@
4444
import org.mockito.Mock;
4545
import org.mockito.Mockito;
4646
import org.mockito.junit.jupiter.MockitoExtension;
47+
4748
import org.springframework.aop.framework.ProxyFactory;
4849
import org.springframework.beans.ConversionNotSupportedException;
4950
import org.springframework.beans.factory.annotation.Autowired;
@@ -2734,6 +2735,23 @@ void shouldReadOpenProjection() {
27342735
assertThat(projection.getName()).isEqualTo("my-book by Walter White");
27352736
}
27362737

2738+
@Test // GH-4120
2739+
void shouldReadDtoProjection() {
2740+
2741+
org.bson.Document author = new org.bson.Document("firstName", "Walter").append("lastName", "White");
2742+
org.bson.Document book = new org.bson.Document("_id", "foo").append("name", "my-book").append("author", author);
2743+
2744+
EntityProjectionIntrospector introspector = EntityProjectionIntrospector.create(converter.getProjectionFactory(),
2745+
EntityProjectionIntrospector.ProjectionPredicate.typeHierarchy()
2746+
.and((target, underlyingType) -> !converter.conversions.isSimpleType(target)),
2747+
mappingContext);
2748+
2749+
AuthorOnly projection = converter.project(introspector.introspect(AuthorOnly.class, Book.class), book);
2750+
2751+
assertThat(projection.getAuthor().getFirstName()).isEqualTo("Walter");
2752+
assertThat(projection.getAuthor().getLastName()).isEqualTo("White");
2753+
}
2754+
27372755
@Test // GH-3596
27382756
void simpleConverter() {
27392757

@@ -3637,6 +3655,21 @@ interface BookProjection {
36373655
String getName();
36383656
}
36393657

3658+
@lombok.Value
3659+
static class AuthorOnly {
3660+
3661+
AuthorNameOnly author;
3662+
}
3663+
3664+
@lombok.Value
3665+
static class AuthorNameOnly {
3666+
3667+
String firstName;
3668+
3669+
String lastName;
3670+
3671+
}
3672+
36403673
@Data
36413674
static class Book {
36423675

0 commit comments

Comments
 (0)