diff --git a/pom.xml b/pom.xml index 81488cfd12..c3c55d7fb1 100644 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ org.springframework.data spring-data-mongodb-parent - 1.8.0.BUILD-SNAPSHOT + 1.8.0.DATAMONGO-1236-SNAPSHOT pom Spring Data MongoDB diff --git a/spring-data-mongodb-cross-store/pom.xml b/spring-data-mongodb-cross-store/pom.xml index 7d18f752fc..e8243dec5c 100644 --- a/spring-data-mongodb-cross-store/pom.xml +++ b/spring-data-mongodb-cross-store/pom.xml @@ -6,7 +6,7 @@ org.springframework.data spring-data-mongodb-parent - 1.8.0.BUILD-SNAPSHOT + 1.8.0.DATAMONGO-1236-SNAPSHOT ../pom.xml @@ -48,7 +48,7 @@ org.springframework.data spring-data-mongodb - 1.8.0.BUILD-SNAPSHOT + 1.8.0.DATAMONGO-1236-SNAPSHOT diff --git a/spring-data-mongodb-distribution/pom.xml b/spring-data-mongodb-distribution/pom.xml index 13c0985a6a..196b8707ef 100644 --- a/spring-data-mongodb-distribution/pom.xml +++ b/spring-data-mongodb-distribution/pom.xml @@ -13,7 +13,7 @@ org.springframework.data spring-data-mongodb-parent - 1.8.0.BUILD-SNAPSHOT + 1.8.0.DATAMONGO-1236-SNAPSHOT ../pom.xml diff --git a/spring-data-mongodb-log4j/pom.xml b/spring-data-mongodb-log4j/pom.xml index 64d6f7864a..30fd9c60e8 100644 --- a/spring-data-mongodb-log4j/pom.xml +++ b/spring-data-mongodb-log4j/pom.xml @@ -5,7 +5,7 @@ org.springframework.data spring-data-mongodb-parent - 1.8.0.BUILD-SNAPSHOT + 1.8.0.DATAMONGO-1236-SNAPSHOT ../pom.xml diff --git a/spring-data-mongodb/pom.xml b/spring-data-mongodb/pom.xml index 819112059b..d52801fea0 100644 --- a/spring-data-mongodb/pom.xml +++ b/spring-data-mongodb/pom.xml @@ -11,7 +11,7 @@ org.springframework.data spring-data-mongodb-parent - 1.8.0.BUILD-SNAPSHOT + 1.8.0.DATAMONGO-1236-SNAPSHOT ../pom.xml diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/UpdateMapper.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/UpdateMapper.java index d00aca5de2..70433cd8b2 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/UpdateMapper.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/UpdateMapper.java @@ -22,6 +22,7 @@ import org.springframework.core.convert.converter.Converter; import org.springframework.data.mapping.Association; import org.springframework.data.mapping.context.MappingContext; +import org.springframework.data.mongodb.core.mapping.BasicMongoPersistentEntity; import org.springframework.data.mongodb.core.mapping.MongoPersistentEntity; import org.springframework.data.mongodb.core.mapping.MongoPersistentProperty; import org.springframework.data.mongodb.core.mapping.MongoPersistentProperty.PropertyToFieldNameConverter; @@ -66,8 +67,8 @@ public UpdateMapper(MongoConverter converter) { */ @Override protected Object delegateConvertToMongoType(Object source, MongoPersistentEntity entity) { - return entity == null ? super.delegateConvertToMongoType(source, null) - : converter.convertToMongoType(source, getTypeHintForEntity(entity)); + return entity == null ? super.delegateConvertToMongoType(source, null) : converter.convertToMongoType(source, + getTypeHintForEntity(source, entity)); } /* @@ -141,18 +142,21 @@ private DBObject getMappedValue(Field field, Modifier modifier) { return new BasicDBObject(modifier.getKey(), value); } - private TypeInformation getTypeHintForEntity(MongoPersistentEntity entity) { - return processTypeHintForNestedDocuments(entity.getTypeInformation()); + private TypeInformation getTypeHintForEntity(Object source, MongoPersistentEntity entity) { + return processTypeHintForNestedDocuments(source, entity.getTypeInformation()); } - private TypeInformation processTypeHintForNestedDocuments(TypeInformation info) { + private TypeInformation processTypeHintForNestedDocuments(Object source, TypeInformation info) { Class type = info.getActualType().getType(); if (type.isInterface() || java.lang.reflect.Modifier.isAbstract(type.getModifiers())) { return info; } - return NESTED_DOCUMENT; + if (!type.equals(source.getClass())) { + return info; + } + return NESTED_DOCUMENT; } /* @@ -196,6 +200,18 @@ public MetadataBackedUpdateField(MongoPersistentEntity entity, String key, this.key = key; } + @Override + @SuppressWarnings({ "rawtypes", "unchecked" }) + public MongoPersistentEntity getPropertyEntity() { + + MongoPersistentEntity entity = super.getPropertyEntity(); + if (entity != null || getProperty() == null) { + return entity; + } + + return new BasicMongoPersistentEntity(getProperty().getTypeInformation()); + } + /* * (non-Javadoc) * @see org.springframework.data.mongodb.core.convert.QueryMapper.MetadataBackedField#getMappedKey() diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/convert/UpdateMapperUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/convert/UpdateMapperUnitTests.java index b1cf95dec1..b2186b6179 100644 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/convert/UpdateMapperUnitTests.java +++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/convert/UpdateMapperUnitTests.java @@ -25,6 +25,7 @@ import java.util.Arrays; import java.util.Collections; import java.util.List; +import java.util.Map; import org.hamcrest.Matcher; import org.hamcrest.collection.IsIterableContainingInOrder; @@ -680,8 +681,85 @@ public void mappingShouldRetainTypeInformationOfNestedListWhenUpdatingConcreteyP context.getPersistentEntity(DomainTypeWrappingConcreteyTypeHavingListOfInterfaceTypeAttributes.class)); assertThat(mappedUpdate, isBsonObject().notContaining("$set.concreteTypeWithListAttributeOfInterfaceType._class")); - assertThat(mappedUpdate, isBsonObject() - .containing("$set.concreteTypeWithListAttributeOfInterfaceType.models.[0]._class", ModelImpl.class.getName())); + assertThat( + mappedUpdate, + isBsonObject().containing("$set.concreteTypeWithListAttributeOfInterfaceType.models.[0]._class", + ModelImpl.class.getName())); + } + + /** + * @see DATAMONGO-1236 + */ + @Test + public void mappingShouldRetainTypeInformationForObjectValues() { + + Update update = new Update().set("value", new NestedDocument("kaladin")); + DBObject mappedUpdate = mapper.getMappedObject(update.getUpdateObject(), + context.getPersistentEntity(EntityWithObject.class)); + + assertThat(mappedUpdate, isBsonObject().containing("$set.value.name", "kaladin")); + assertThat(mappedUpdate, isBsonObject().containing("$set.value._class", NestedDocument.class.getName())); + } + + /** + * @see DATAMONGO-1236 + */ + @Test + public void mappingShouldNotRetainTypeInformationForConcreteValues() { + + Update update = new Update().set("concreteValue", new NestedDocument("shallan")); + DBObject mappedUpdate = mapper.getMappedObject(update.getUpdateObject(), + context.getPersistentEntity(EntityWithObject.class)); + + assertThat(mappedUpdate, isBsonObject().containing("$set.concreteValue.name", "shallan")); + assertThat(mappedUpdate, isBsonObject().notContaining("$set.concreteValue._class")); + } + + /** + * @see DATAMONGO-1236 + */ + @Test + public void mappingShouldRetainTypeInformationForObjectValuesWithAlias() { + + Update update = new Update().set("value", new NestedDocument("adolin")); + DBObject mappedUpdate = mapper.getMappedObject(update.getUpdateObject(), + context.getPersistentEntity(EntityWithAliasedObject.class)); + + assertThat(mappedUpdate, isBsonObject().containing("$set.renamed-value.name", "adolin")); + assertThat(mappedUpdate, isBsonObject().containing("$set.renamed-value._class", NestedDocument.class.getName())); + } + + /** + * @see DATAMONGO-1236 + */ + @Test + public void mappingShouldRetrainTypeInformationWhenValueTypeOfMapDoesNotMatchItsDeclaration() { + + Map map = Collections. singletonMap("szeth", new NestedDocument("son-son-vallano")); + + Update update = new Update().set("map", map); + DBObject mappedUpdate = mapper.getMappedObject(update.getUpdateObject(), + context.getPersistentEntity(EntityWithObjectMap.class)); + + assertThat(mappedUpdate, isBsonObject().containing("$set.map.szeth.name", "son-son-vallano")); + assertThat(mappedUpdate, isBsonObject().containing("$set.map.szeth._class", NestedDocument.class.getName())); + } + + /** + * @see DATAMONGO-1236 + */ + @Test + public void mappingShouldNotContainTypeInformationWhenValueTypeOfMapMatchesDeclaration() { + + Map map = Collections. singletonMap("jasnah", new NestedDocument( + "kholin")); + + Update update = new Update().set("concreteMap", map); + DBObject mappedUpdate = mapper.getMappedObject(update.getUpdateObject(), + context.getPersistentEntity(EntityWithObjectMap.class)); + + assertThat(mappedUpdate, isBsonObject().containing("$set.concreteMap.jasnah.name", "kholin")); + assertThat(mappedUpdate, isBsonObject().notContaining("$set.concreteMap.jasnah._class")); } static class DomainTypeWrappingConcreteyTypeHavingListOfInterfaceTypeAttributes { @@ -889,4 +967,21 @@ public NestedDocument(String name) { this.name = name; } } + + static class EntityWithObject { + + Object value; + NestedDocument concreteValue; + } + + static class EntityWithAliasedObject { + + @Field("renamed-value") Object value; + } + + static class EntityWithObjectMap { + + Map map; + Map concreteMap; + } }