Skip to content

Commit cb0b960

Browse files
christophstroblodrotbohm
authored andcommitted
DATAMONGO-1236 - Update now include type hint correctly.
We now use property type information when mapping fields affected by an update in case we do not have proper entity information within the context. This allows more precise type resolution required for determining the need to write type hints for a given property. Original pull request: spring-projects#301.
1 parent 1dbe3b6 commit cb0b960

File tree

2 files changed

+119
-8
lines changed

2 files changed

+119
-8
lines changed

spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/UpdateMapper.java

Lines changed: 22 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
import org.springframework.core.convert.converter.Converter;
2323
import org.springframework.data.mapping.Association;
2424
import org.springframework.data.mapping.context.MappingContext;
25+
import org.springframework.data.mongodb.core.mapping.BasicMongoPersistentEntity;
2526
import org.springframework.data.mongodb.core.mapping.MongoPersistentEntity;
2627
import org.springframework.data.mongodb.core.mapping.MongoPersistentProperty;
2728
import org.springframework.data.mongodb.core.mapping.MongoPersistentProperty.PropertyToFieldNameConverter;
@@ -66,8 +67,8 @@ public UpdateMapper(MongoConverter converter) {
6667
*/
6768
@Override
6869
protected Object delegateConvertToMongoType(Object source, MongoPersistentEntity<?> entity) {
69-
return entity == null ? super.delegateConvertToMongoType(source, null)
70-
: converter.convertToMongoType(source, getTypeHintForEntity(entity));
70+
return entity == null ? super.delegateConvertToMongoType(source, null) : converter.convertToMongoType(source,
71+
getTypeHintForEntity(source, entity));
7172
}
7273

7374
/*
@@ -141,18 +142,21 @@ private DBObject getMappedValue(Field field, Modifier modifier) {
141142
return new BasicDBObject(modifier.getKey(), value);
142143
}
143144

144-
private TypeInformation<?> getTypeHintForEntity(MongoPersistentEntity<?> entity) {
145-
return processTypeHintForNestedDocuments(entity.getTypeInformation());
145+
private TypeInformation<?> getTypeHintForEntity(Object source, MongoPersistentEntity<?> entity) {
146+
return processTypeHintForNestedDocuments(source, entity.getTypeInformation());
146147
}
147148

148-
private TypeInformation<?> processTypeHintForNestedDocuments(TypeInformation<?> info) {
149+
private TypeInformation<?> processTypeHintForNestedDocuments(Object source, TypeInformation<?> info) {
149150

150151
Class<?> type = info.getActualType().getType();
151152
if (type.isInterface() || java.lang.reflect.Modifier.isAbstract(type.getModifiers())) {
152153
return info;
153154
}
154-
return NESTED_DOCUMENT;
155155

156+
if (!type.equals(source.getClass())) {
157+
return info;
158+
}
159+
return NESTED_DOCUMENT;
156160
}
157161

158162
/*
@@ -196,6 +200,18 @@ public MetadataBackedUpdateField(MongoPersistentEntity<?> entity, String key,
196200
this.key = key;
197201
}
198202

203+
@Override
204+
@SuppressWarnings({ "rawtypes", "unchecked" })
205+
public MongoPersistentEntity<?> getPropertyEntity() {
206+
207+
MongoPersistentEntity<?> entity = super.getPropertyEntity();
208+
if (entity != null || getProperty() == null) {
209+
return entity;
210+
}
211+
212+
return new BasicMongoPersistentEntity(getProperty().getTypeInformation());
213+
}
214+
199215
/*
200216
* (non-Javadoc)
201217
* @see org.springframework.data.mongodb.core.convert.QueryMapper.MetadataBackedField#getMappedKey()

spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/convert/UpdateMapperUnitTests.java

Lines changed: 97 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
import java.util.Arrays;
2626
import java.util.Collections;
2727
import java.util.List;
28+
import java.util.Map;
2829

2930
import org.hamcrest.Matcher;
3031
import org.hamcrest.collection.IsIterableContainingInOrder;
@@ -680,8 +681,85 @@ public void mappingShouldRetainTypeInformationOfNestedListWhenUpdatingConcreteyP
680681
context.getPersistentEntity(DomainTypeWrappingConcreteyTypeHavingListOfInterfaceTypeAttributes.class));
681682

682683
assertThat(mappedUpdate, isBsonObject().notContaining("$set.concreteTypeWithListAttributeOfInterfaceType._class"));
683-
assertThat(mappedUpdate, isBsonObject()
684-
.containing("$set.concreteTypeWithListAttributeOfInterfaceType.models.[0]._class", ModelImpl.class.getName()));
684+
assertThat(
685+
mappedUpdate,
686+
isBsonObject().containing("$set.concreteTypeWithListAttributeOfInterfaceType.models.[0]._class",
687+
ModelImpl.class.getName()));
688+
}
689+
690+
/**
691+
* @see DATAMONGO-1236
692+
*/
693+
@Test
694+
public void mappingShouldRetainTypeInformationForObjectValues() {
695+
696+
Update update = new Update().set("value", new NestedDocument("kaladin"));
697+
DBObject mappedUpdate = mapper.getMappedObject(update.getUpdateObject(),
698+
context.getPersistentEntity(EntityWithObject.class));
699+
700+
assertThat(mappedUpdate, isBsonObject().containing("$set.value.name", "kaladin"));
701+
assertThat(mappedUpdate, isBsonObject().containing("$set.value._class", NestedDocument.class.getName()));
702+
}
703+
704+
/**
705+
* @see DATAMONGO-1236
706+
*/
707+
@Test
708+
public void mappingShouldNotRetainTypeInformationForConcreteValues() {
709+
710+
Update update = new Update().set("concreteValue", new NestedDocument("shallan"));
711+
DBObject mappedUpdate = mapper.getMappedObject(update.getUpdateObject(),
712+
context.getPersistentEntity(EntityWithObject.class));
713+
714+
assertThat(mappedUpdate, isBsonObject().containing("$set.concreteValue.name", "shallan"));
715+
assertThat(mappedUpdate, isBsonObject().notContaining("$set.concreteValue._class"));
716+
}
717+
718+
/**
719+
* @see DATAMONGO-1236
720+
*/
721+
@Test
722+
public void mappingShouldRetainTypeInformationForObjectValuesWithAlias() {
723+
724+
Update update = new Update().set("value", new NestedDocument("adolin"));
725+
DBObject mappedUpdate = mapper.getMappedObject(update.getUpdateObject(),
726+
context.getPersistentEntity(EntityWithAliasedObject.class));
727+
728+
assertThat(mappedUpdate, isBsonObject().containing("$set.renamed-value.name", "adolin"));
729+
assertThat(mappedUpdate, isBsonObject().containing("$set.renamed-value._class", NestedDocument.class.getName()));
730+
}
731+
732+
/**
733+
* @see DATAMONGO-1236
734+
*/
735+
@Test
736+
public void mappingShouldRetrainTypeInformationWhenValueTypeOfMapDoesNotMatchItsDeclaration() {
737+
738+
Map<Object, Object> map = Collections.<Object, Object> singletonMap("szeth", new NestedDocument("son-son-vallano"));
739+
740+
Update update = new Update().set("map", map);
741+
DBObject mappedUpdate = mapper.getMappedObject(update.getUpdateObject(),
742+
context.getPersistentEntity(EntityWithObjectMap.class));
743+
744+
assertThat(mappedUpdate, isBsonObject().containing("$set.map.szeth.name", "son-son-vallano"));
745+
assertThat(mappedUpdate, isBsonObject().containing("$set.map.szeth._class", NestedDocument.class.getName()));
746+
}
747+
748+
/**
749+
* @see DATAMONGO-1236
750+
*/
751+
@Test
752+
public void mappingShouldNotContainTypeInformationWhenValueTypeOfMapMatchesDeclaration() {
753+
754+
Map<Object, NestedDocument> map = Collections.<Object, NestedDocument> singletonMap("jasnah", new NestedDocument(
755+
"kholin"));
756+
757+
Update update = new Update().set("concreteMap", map);
758+
DBObject mappedUpdate = mapper.getMappedObject(update.getUpdateObject(),
759+
context.getPersistentEntity(EntityWithObjectMap.class));
760+
761+
assertThat(mappedUpdate, isBsonObject().containing("$set.concreteMap.jasnah.name", "kholin"));
762+
assertThat(mappedUpdate, isBsonObject().notContaining("$set.concreteMap.jasnah._class"));
685763
}
686764

687765
static class DomainTypeWrappingConcreteyTypeHavingListOfInterfaceTypeAttributes {
@@ -889,4 +967,21 @@ public NestedDocument(String name) {
889967
this.name = name;
890968
}
891969
}
970+
971+
static class EntityWithObject {
972+
973+
Object value;
974+
NestedDocument concreteValue;
975+
}
976+
977+
static class EntityWithAliasedObject {
978+
979+
@Field("renamed-value") Object value;
980+
}
981+
982+
static class EntityWithObjectMap {
983+
984+
Map<Object, Object> map;
985+
Map<Object, NestedDocument> concreteMap;
986+
}
892987
}

0 commit comments

Comments
 (0)