Skip to content

Commit 21057c3

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 d0a98eb commit 21057c3

File tree

2 files changed

+56
-12
lines changed

2 files changed

+56
-12
lines changed

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

+23-12
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,16 @@
1717

1818
import java.lang.reflect.Constructor;
1919
import java.lang.reflect.Method;
20-
import java.util.*;
20+
import java.util.ArrayList;
21+
import java.util.Arrays;
22+
import java.util.Collection;
23+
import java.util.Collections;
24+
import java.util.HashSet;
25+
import java.util.LinkedHashMap;
26+
import java.util.List;
27+
import java.util.Map;
28+
import java.util.Optional;
29+
import java.util.Set;
2130
import java.util.function.Predicate;
2231
import java.util.stream.Collectors;
2332

@@ -30,6 +39,7 @@
3039
import org.bson.conversions.Bson;
3140
import org.bson.json.JsonReader;
3241
import org.bson.types.ObjectId;
42+
3343
import org.springframework.beans.BeansException;
3444
import org.springframework.beans.factory.BeanClassLoaderAware;
3545
import org.springframework.context.ApplicationContext;
@@ -395,8 +405,8 @@ public ConversionContext forProperty(String name) {
395405

396406
EntityProjection<?, ?> property = returnedTypeDescriptor.findProperty(name);
397407
if (property == null) {
398-
return new ConversionContext(sourceConverter, conversions, path, MappingMongoConverter.this::readDocument, collectionConverter,
399-
mapConverter, dbRefConverter, elementConverter);
408+
return new ConversionContext(sourceConverter, conversions, path, MappingMongoConverter.this::readDocument,
409+
collectionConverter, mapConverter, dbRefConverter, elementConverter);
400410
}
401411

402412
return new ProjectingConversionContext(sourceConverter, conversions, path, collectionConverter, mapConverter,
@@ -938,9 +948,8 @@ protected void writePropertyInternal(@Nullable Object obj, DocumentAccessor acce
938948
TypeInformation<?> type = prop.getTypeInformation();
939949

940950
if (conversions.hasValueConverter(prop)) {
941-
accessor.put(prop,
942-
conversions.getPropertyValueConversions().getValueConverter(prop)
943-
.write(obj, new MongoConversionContext(prop, this)));
951+
accessor.put(prop, conversions.getPropertyValueConversions().getValueConverter(prop).write(obj,
952+
new MongoConversionContext(prop, this)));
944953
return;
945954
}
946955

@@ -1275,9 +1284,8 @@ private void writeSimpleInternal(@Nullable Object value, Bson bson, MongoPersist
12751284
DocumentAccessor accessor = new DocumentAccessor(bson);
12761285

12771286
if (conversions.hasValueConverter(property)) {
1278-
accessor.put(property,
1279-
conversions.getPropertyValueConversions().getValueConverter(property)
1280-
.write(value, new MongoConversionContext(property, this)));
1287+
accessor.put(property, conversions.getPropertyValueConversions().getValueConverter(property).write(value,
1288+
new MongoConversionContext(property, this)));
12811289
return;
12821290
}
12831291

@@ -1927,12 +1935,15 @@ public <T> T getPropertyValue(MongoPersistentProperty property) {
19271935
return null;
19281936
}
19291937

1930-
if (context.conversions.hasValueConverter(property)) {
1931-
return (T) context.conversions.getPropertyValueConversions().getValueConverter(property).read(value,
1938+
CustomConversions conversions = context.conversions;
1939+
if (conversions.hasValueConverter(property)) {
1940+
return (T) conversions.getPropertyValueConversions().getValueConverter(property).read(value,
19321941
new MongoConversionContext(property, context.sourceConverter));
19331942
}
19341943

1935-
return (T) context.convert(value, property.getTypeInformation());
1944+
ConversionContext contextToUse = context.forProperty(property.getName());
1945+
1946+
return (T) contextToUse.convert(value, property.getTypeInformation());
19361947
}
19371948

19381949
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
@@ -45,6 +45,7 @@
4545
import org.mockito.Mock;
4646
import org.mockito.Mockito;
4747
import org.mockito.junit.jupiter.MockitoExtension;
48+
4849
import org.springframework.aop.framework.ProxyFactory;
4950
import org.springframework.beans.ConversionNotSupportedException;
5051
import org.springframework.beans.factory.annotation.Autowired;
@@ -2707,6 +2708,23 @@ void shouldReadOpenProjection() {
27072708
assertThat(projection.getName()).isEqualTo("my-book by Walter White");
27082709
}
27092710

2711+
@Test // GH-4120
2712+
void shouldReadDtoProjection() {
2713+
2714+
org.bson.Document author = new org.bson.Document("firstName", "Walter").append("lastName", "White");
2715+
org.bson.Document book = new org.bson.Document("_id", "foo").append("name", "my-book").append("author", author);
2716+
2717+
EntityProjectionIntrospector introspector = EntityProjectionIntrospector.create(converter.getProjectionFactory(),
2718+
EntityProjectionIntrospector.ProjectionPredicate.typeHierarchy()
2719+
.and((target, underlyingType) -> !converter.conversions.isSimpleType(target)),
2720+
mappingContext);
2721+
2722+
AuthorOnly projection = converter.project(introspector.introspect(AuthorOnly.class, Book.class), book);
2723+
2724+
assertThat(projection.getAuthor().getFirstName()).isEqualTo("Walter");
2725+
assertThat(projection.getAuthor().getLastName()).isEqualTo("White");
2726+
}
2727+
27102728
@Test // GH-3596
27112729
void simpleConverter() {
27122730

@@ -3610,6 +3628,21 @@ interface BookProjection {
36103628
String getName();
36113629
}
36123630

3631+
@lombok.Value
3632+
static class AuthorOnly {
3633+
3634+
AuthorNameOnly author;
3635+
}
3636+
3637+
@lombok.Value
3638+
static class AuthorNameOnly {
3639+
3640+
String firstName;
3641+
3642+
String lastName;
3643+
3644+
}
3645+
36133646
@Data
36143647
static class Book {
36153648

0 commit comments

Comments
 (0)