Create a new conversion context for projection properties.
We now create a new conversion context to ensure that we use the correct property type to avoid type retention when mapping complex objects within a projection. Closes #3998
This commit is contained in:
@@ -39,7 +39,16 @@ import org.springframework.core.convert.support.DefaultConversionService;
|
||||
import org.springframework.data.annotation.Reference;
|
||||
import org.springframework.data.convert.CustomConversions;
|
||||
import org.springframework.data.convert.TypeMapper;
|
||||
import org.springframework.data.mapping.*;
|
||||
import org.springframework.data.mapping.AccessOptions;
|
||||
import org.springframework.data.mapping.Association;
|
||||
import org.springframework.data.mapping.MappingException;
|
||||
import org.springframework.data.mapping.Parameter;
|
||||
import org.springframework.data.mapping.PersistentEntity;
|
||||
import org.springframework.data.mapping.PersistentProperty;
|
||||
import org.springframework.data.mapping.PersistentPropertyAccessor;
|
||||
import org.springframework.data.mapping.PersistentPropertyPath;
|
||||
import org.springframework.data.mapping.PersistentPropertyPathAccessor;
|
||||
import org.springframework.data.mapping.PreferredConstructor;
|
||||
import org.springframework.data.mapping.callback.EntityCallbacks;
|
||||
import org.springframework.data.mapping.context.MappingContext;
|
||||
import org.springframework.data.mapping.model.ConvertingPropertyAccessor;
|
||||
@@ -385,7 +394,8 @@ public class MappingMongoConverter extends AbstractMongoConverter implements App
|
||||
|
||||
EntityProjection<?, ?> property = returnedTypeDescriptor.findProperty(name);
|
||||
if (property == null) {
|
||||
return super.forProperty(name);
|
||||
return new ConversionContext(conversions, path, MappingMongoConverter.this::readDocument, collectionConverter,
|
||||
mapConverter, dbRefConverter, elementConverter);
|
||||
}
|
||||
|
||||
return new ProjectingConversionContext(conversions, path, collectionConverter, mapConverter, dbRefConverter,
|
||||
@@ -1902,12 +1912,8 @@ public class MappingMongoConverter extends AbstractMongoConverter implements App
|
||||
}
|
||||
|
||||
public MongoDbPropertyValueProvider withContext(ConversionContext context) {
|
||||
if (context == this.context) {
|
||||
return this;
|
||||
}
|
||||
|
||||
return new MongoDbPropertyValueProvider(context, accessor, evaluator);
|
||||
|
||||
return context == this.context ? this : new MongoDbPropertyValueProvider(context, accessor, evaluator);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -20,6 +20,7 @@ import static org.assertj.core.api.Assertions.*;
|
||||
import static org.mockito.Mockito.*;
|
||||
import static org.springframework.data.mongodb.core.DocumentTestUtils.*;
|
||||
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.Getter;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
@@ -2664,8 +2665,8 @@ class MappingMongoConverterUnitTests {
|
||||
.and((target, underlyingType) -> !converter.conversions.isSimpleType(target)),
|
||||
mappingContext);
|
||||
|
||||
EntityProjection<WithNestedProjection, Person> projection = introspector
|
||||
.introspect(WithNestedProjection.class, Person.class);
|
||||
EntityProjection<WithNestedProjection, Person> projection = introspector.introspect(WithNestedProjection.class,
|
||||
Person.class);
|
||||
WithNestedProjection person = converter.project(projection, source);
|
||||
|
||||
assertThat(person.getAddresses()).extracting(AddressProjection::getStreet).hasSize(1).containsOnly("hwy");
|
||||
@@ -2689,6 +2690,22 @@ class MappingMongoConverterUnitTests {
|
||||
assertThat(person.getAddresses()).extracting(Address::getStreet).hasSize(1).containsOnly("hwy");
|
||||
}
|
||||
|
||||
@Test // GH-3998
|
||||
void shouldReadOpenProjection() {
|
||||
|
||||
org.bson.Document author = new org.bson.Document("firstName", "Walter").append("lastName", "White");
|
||||
org.bson.Document book = new org.bson.Document("_id", "foo").append("name", "my-book").append("author", author);
|
||||
|
||||
EntityProjectionIntrospector introspector = EntityProjectionIntrospector.create(converter.getProjectionFactory(),
|
||||
EntityProjectionIntrospector.ProjectionPredicate.typeHierarchy()
|
||||
.and((target, underlyingType) -> !converter.conversions.isSimpleType(target)),
|
||||
mappingContext);
|
||||
|
||||
BookProjection projection = converter.project(introspector.introspect(BookProjection.class, Book.class), book);
|
||||
|
||||
assertThat(projection.getName()).isEqualTo("my-book by Walter White");
|
||||
}
|
||||
|
||||
static class GenericType<T> {
|
||||
T content;
|
||||
}
|
||||
@@ -3413,11 +3430,56 @@ class MappingMongoConverterUnitTests {
|
||||
@org.springframework.data.mongodb.core.mapping.Field(
|
||||
write = org.springframework.data.mongodb.core.mapping.Field.Write.ALWAYS) Integer writeAlways;
|
||||
|
||||
@org.springframework.data.mongodb.core.mapping.DBRef @org.springframework.data.mongodb.core.mapping.Field(
|
||||
@org.springframework.data.mongodb.core.mapping.DBRef
|
||||
@org.springframework.data.mongodb.core.mapping.Field(
|
||||
write = org.springframework.data.mongodb.core.mapping.Field.Write.NON_NULL) Person writeNonNullPerson;
|
||||
|
||||
@org.springframework.data.mongodb.core.mapping.DBRef @org.springframework.data.mongodb.core.mapping.Field(
|
||||
@org.springframework.data.mongodb.core.mapping.DBRef
|
||||
@org.springframework.data.mongodb.core.mapping.Field(
|
||||
write = org.springframework.data.mongodb.core.mapping.Field.Write.ALWAYS) Person writeAlwaysPerson;
|
||||
|
||||
}
|
||||
|
||||
interface BookProjection {
|
||||
|
||||
@Value("#{target.name + ' by ' + target.author.firstName + ' ' + target.author.lastName}")
|
||||
String getName();
|
||||
}
|
||||
|
||||
@Data
|
||||
static class Book {
|
||||
|
||||
@Id String id;
|
||||
|
||||
String name;
|
||||
|
||||
Author author = new Author();
|
||||
|
||||
public Book() {}
|
||||
|
||||
public Book(String id, String name, Author author) {
|
||||
this.id = id;
|
||||
this.name = name;
|
||||
this.author = author;
|
||||
}
|
||||
}
|
||||
|
||||
static class Author {
|
||||
|
||||
@Id String id;
|
||||
|
||||
String firstName;
|
||||
|
||||
String lastName;
|
||||
|
||||
public Author() {}
|
||||
|
||||
public Author(String id, String firstName, String lastName) {
|
||||
this.id = id;
|
||||
this.firstName = firstName;
|
||||
this.lastName = lastName;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user