From e6428b229dd96f05d558291159c4901300de5c43 Mon Sep 17 00:00:00 2001 From: Christoph Strobl Date: Mon, 15 Jun 2015 09:45:27 +0200 Subject: [PATCH 1/2] DATAMONGO-1236 - Update does not include type hint correctly. Prepare issue branch. --- pom.xml | 2 +- spring-data-mongodb-cross-store/pom.xml | 4 ++-- spring-data-mongodb-distribution/pom.xml | 2 +- spring-data-mongodb-log4j/pom.xml | 2 +- spring-data-mongodb/pom.xml | 2 +- 5 files changed, 6 insertions(+), 6 deletions(-) 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 From ca6f80c5373f981c5801a71ffede5d8b6e1bb901 Mon Sep 17 00:00:00 2001 From: Christoph Strobl Date: Mon, 15 Jun 2015 09:51:39 +0200 Subject: [PATCH 2/2] DATAMONGO-1236 - Update does not 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. --- .../mongodb/core/convert/UpdateMapper.java | 28 ++++-- .../core/convert/UpdateMapperUnitTests.java | 99 ++++++++++++++++++- 2 files changed, 119 insertions(+), 8 deletions(-) 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; + } }