diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/MappingMongoConverter.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/MappingMongoConverter.java index ea0d35678..7775767dc 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/MappingMongoConverter.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/MappingMongoConverter.java @@ -136,8 +136,8 @@ public class MappingMongoConverter extends AbstractMongoConverter implements App * @param typeMapper the typeMapper to set */ public void setTypeMapper(MongoTypeMapper typeMapper) { - this.typeMapper = typeMapper == null ? new DefaultMongoTypeMapper(DefaultMongoTypeMapper.DEFAULT_TYPE_KEY, - mappingContext) : typeMapper; + this.typeMapper = typeMapper == null + ? new DefaultMongoTypeMapper(DefaultMongoTypeMapper.DEFAULT_TYPE_KEY, mappingContext) : typeMapper; } /* @@ -238,7 +238,8 @@ public class MappingMongoConverter extends AbstractMongoConverter implements App PersistentEntityParameterValueProvider parameterProvider = new PersistentEntityParameterValueProvider( entity, provider, path.getCurrentObject()); - return new ConverterAwareSpELExpressionParameterValueProvider(evaluator, conversionService, parameterProvider, path); + return new ConverterAwareSpELExpressionParameterValueProvider(evaluator, conversionService, parameterProvider, + path); } private S read(final MongoPersistentEntity entity, final DBObject dbo, final ObjectPath path) { @@ -510,8 +511,8 @@ public class MappingMongoConverter extends AbstractMongoConverter implements App : new BasicDBObject(); addCustomTypeKeyIfNecessary(ClassTypeInformation.from(prop.getRawType()), obj, propDbObj); - MongoPersistentEntity entity = isSubtype(prop.getType(), obj.getClass()) ? mappingContext - .getPersistentEntity(obj.getClass()) : mappingContext.getPersistentEntity(type); + MongoPersistentEntity entity = isSubtype(prop.getType(), obj.getClass()) + ? mappingContext.getPersistentEntity(obj.getClass()) : mappingContext.getPersistentEntity(type); writeInternal(obj, propDbObj, entity); accessor.put(prop, propDbObj); @@ -700,8 +701,10 @@ public class MappingMongoConverter extends AbstractMongoConverter implements App } if (mapKeyDotReplacement == null) { - throw new MappingException(String.format("Map key %s contains dots but no replacement was configured! Make " - + "sure map keys don't contain dots in the first place or configure an appropriate replacement!", source)); + throw new MappingException(String.format( + "Map key %s contains dots but no replacement was configured! Make " + + "sure map keys don't contain dots in the first place or configure an appropriate replacement!", + source)); } return source.replaceAll("\\.", mapKeyDotReplacement); @@ -719,8 +722,8 @@ public class MappingMongoConverter extends AbstractMongoConverter implements App return (String) key; } - return conversions.hasCustomWriteTarget(key.getClass(), String.class) ? (String) getPotentiallyConvertedSimpleWrite(key) - : key.toString(); + return conversions.hasCustomWriteTarget(key.getClass(), String.class) + ? (String) getPotentiallyConvertedSimpleWrite(key) : key.toString(); } /** @@ -889,16 +892,16 @@ public class MappingMongoConverter extends AbstractMongoConverter implements App Class rawComponentType = componentType == null ? null : componentType.getType(); collectionType = Collection.class.isAssignableFrom(collectionType) ? collectionType : List.class; - Collection items = targetType.getType().isArray() ? new ArrayList() : CollectionFactory - .createCollection(collectionType, rawComponentType, sourceValue.size()); + Collection items = targetType.getType().isArray() ? new ArrayList() + : CollectionFactory.createCollection(collectionType, rawComponentType, sourceValue.size()); for (int i = 0; i < sourceValue.size(); i++) { Object dbObjItem = sourceValue.get(i); if (dbObjItem instanceof DBRef) { - items.add(DBRef.class.equals(rawComponentType) ? dbObjItem : read(componentType, readRef((DBRef) dbObjItem), - path)); + items.add( + DBRef.class.equals(rawComponentType) ? dbObjItem : read(componentType, readRef((DBRef) dbObjItem), path)); } else if (dbObjItem instanceof DBObject) { items.add(read(componentType, (DBObject) dbObjItem, path)); } else { @@ -1016,14 +1019,14 @@ public class MappingMongoConverter extends AbstractMongoConverter implements App this.write(obj, newDbo); if (typeInformation == null) { - return removeTypeInfoRecursively(newDbo); + return removeTypeInfo(newDbo, true); } if (typeInformation.getType().equals(NestedDocument.class)) { - return removeTypeInfo(newDbo); + return removeTypeInfo(newDbo, false); } - return !obj.getClass().equals(typeInformation.getType()) ? newDbo : removeTypeInfoRecursively(newDbo); + return !obj.getClass().equals(typeInformation.getType()) ? newDbo : removeTypeInfo(newDbo, true); } public BasicDBList maybeConvertList(Iterable source, TypeInformation typeInformation) { @@ -1037,12 +1040,13 @@ public class MappingMongoConverter extends AbstractMongoConverter implements App } /** - * Removes only the type information from the root document. + * Removes the type information from the entire conversion result. * * @param object + * @param recursively whether to apply the removal recursively * @return */ - private Object removeTypeInfo(Object object) { + private Object removeTypeInfo(Object object, boolean recursively) { if (!(object instanceof DBObject)) { return object; @@ -1050,47 +1054,29 @@ public class MappingMongoConverter extends AbstractMongoConverter implements App DBObject dbObject = (DBObject) object; String keyToRemove = null; + for (String key : dbObject.keySet()) { - if (typeMapper.isTypeKey(key)) { - keyToRemove = key; - } - } + if (recursively) { - if (keyToRemove != null) { - dbObject.removeField(keyToRemove); - } + Object value = dbObject.get(key); - return dbObject; - } - - /** - * Removes the type information from the entire conversion result. - * - * @param object - * @return - */ - private Object removeTypeInfoRecursively(Object object) { - - if (!(object instanceof DBObject)) { - return object; - } - - DBObject dbObject = (DBObject) object; - String keyToRemove = null; - for (String key : dbObject.keySet()) { + if (value instanceof BasicDBList) { + for (Object element : (BasicDBList) value) { + removeTypeInfo(element, recursively); + } + } else { + removeTypeInfo(value, recursively); + } + } if (typeMapper.isTypeKey(key)) { + keyToRemove = key; - } - Object value = dbObject.get(key); - if (value instanceof BasicDBList) { - for (Object element : (BasicDBList) value) { - removeTypeInfoRecursively(element); + if (!recursively) { + break; } - } else { - removeTypeInfoRecursively(value); } } @@ -1154,8 +1140,8 @@ public class MappingMongoConverter extends AbstractMongoConverter implements App * * @author Oliver Gierke */ - private class ConverterAwareSpELExpressionParameterValueProvider extends - SpELExpressionParameterValueProvider { + private class ConverterAwareSpELExpressionParameterValueProvider + extends SpELExpressionParameterValueProvider { private final ObjectPath path; @@ -1167,7 +1153,8 @@ public class MappingMongoConverter extends AbstractMongoConverter implements App * @param delegate must not be {@literal null}. */ public ConverterAwareSpELExpressionParameterValueProvider(SpELExpressionEvaluator evaluator, - ConversionService conversionService, ParameterValueProvider delegate, ObjectPath path) { + ConversionService conversionService, ParameterValueProvider delegate, + ObjectPath path) { super(evaluator, conversionService, delegate); this.path = path; diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/QueryMapper.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/QueryMapper.java index b6f1292c0..bd5a5ccfe 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/QueryMapper.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/QueryMapper.java @@ -34,10 +34,13 @@ import org.springframework.data.mapping.PropertyReferenceException; import org.springframework.data.mapping.context.MappingContext; import org.springframework.data.mapping.context.PersistentPropertyPath; import org.springframework.data.mapping.model.MappingException; +import org.springframework.data.mongodb.core.convert.MappingMongoConverter.NestedDocument; 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; import org.springframework.data.mongodb.core.query.Query; +import org.springframework.data.util.ClassTypeInformation; +import org.springframework.data.util.TypeInformation; import org.springframework.util.Assert; import com.mongodb.BasicDBList; @@ -58,6 +61,7 @@ public class QueryMapper { private static final List DEFAULT_ID_NAMES = Arrays.asList("id", "_id"); private static final DBObject META_TEXT_SCORE = new BasicDBObject("$meta", "textScore"); + static final ClassTypeInformation NESTED_DOCUMENT = ClassTypeInformation.from(NestedDocument.class); private enum MetaMapping { FORCE, WHEN_PRESENT, IGNORE; @@ -250,8 +254,8 @@ public class QueryMapper { boolean needsAssociationConversion = property.isAssociation() && !keyword.isExists(); Object value = keyword.getValue(); - Object convertedValue = needsAssociationConversion ? convertAssociation(value, property) : getMappedValue( - property.with(keyword.getKey()), value); + Object convertedValue = needsAssociationConversion ? convertAssociation(value, property) + : getMappedValue(property.with(keyword.getKey()), value); return new BasicDBObject(keyword.key, convertedValue); } @@ -473,8 +477,8 @@ public class QueryMapper { } try { - return conversionService.canConvert(id.getClass(), ObjectId.class) ? conversionService - .convert(id, ObjectId.class) : delegateConvertToMongoType(id, null); + return conversionService.canConvert(id.getClass(), ObjectId.class) ? conversionService.convert(id, ObjectId.class) + : delegateConvertToMongoType(id, null); } catch (ConversionException o_O) { return delegateConvertToMongoType(id, null); } @@ -667,6 +671,10 @@ public class QueryMapper { public Association getAssociation() { return null; } + + public TypeInformation getTypeHint() { + return ClassTypeInformation.OBJECT; + } } /** @@ -872,6 +880,27 @@ public class QueryMapper { protected Converter getAssociationConverter() { return new AssociationConverter(getAssociation()); } + + /* + * (non-Javadoc) + * @see org.springframework.data.mongodb.core.convert.QueryMapper.Field#getTypeHint() + */ + @Override + public TypeInformation getTypeHint() { + + MongoPersistentProperty property = getProperty(); + + if (property == null) { + return super.getTypeHint(); + } + + if (property.getActualType().isInterface() + || java.lang.reflect.Modifier.isAbstract(property.getActualType().getModifiers())) { + return ClassTypeInformation.OBJECT; + } + + return NESTED_DOCUMENT; + } } /** 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 d3e89c6ba..d00aca5de 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,7 +22,6 @@ import java.util.Map.Entry; 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.convert.MappingMongoConverter.NestedDocument; 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; @@ -45,7 +44,6 @@ import com.mongodb.DBObject; */ public class UpdateMapper extends QueryMapper { - private static final ClassTypeInformation NESTED_DOCUMENT = ClassTypeInformation.from(NestedDocument.class); private final MongoConverter converter; /** @@ -68,8 +66,8 @@ public class UpdateMapper extends QueryMapper { */ @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(entity)); } /* @@ -137,22 +135,10 @@ public class UpdateMapper extends QueryMapper { private DBObject getMappedValue(Field field, Modifier modifier) { - Object value = converter.convertToMongoType(modifier.getValue(), getTypeHintForField(field)); - return new BasicDBObject(modifier.getKey(), value); - } - - private TypeInformation getTypeHintForField(Field field) { - - if (field == null || field.getProperty() == null) { - return ClassTypeInformation.OBJECT; - } + TypeInformation typeHint = field == null ? ClassTypeInformation.OBJECT : field.getTypeHint(); - if (field.getProperty().getActualType().isInterface() - || java.lang.reflect.Modifier.isAbstract(field.getProperty().getActualType().getModifiers())) { - return ClassTypeInformation.OBJECT; - } - - return NESTED_DOCUMENT; + Object value = converter.convertToMongoType(modifier.getValue(), typeHint); + return new BasicDBObject(modifier.getKey(), value); } private TypeInformation getTypeHintForEntity(MongoPersistentEntity entity) { @@ -177,8 +163,8 @@ public class UpdateMapper extends QueryMapper { protected Field createPropertyField(MongoPersistentEntity entity, String key, MappingContext, MongoPersistentProperty> mappingContext) { - return entity == null ? super.createPropertyField(entity, key, mappingContext) : // - new MetadataBackedUpdateField(entity, key, mappingContext); + return entity == null ? super.createPropertyField(entity, key, mappingContext) + : new MetadataBackedUpdateField(entity, key, mappingContext); } /** 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 b88b773fc..b1cf95dec 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 @@ -430,8 +430,8 @@ public class UpdateMapperUnitTests { public void rendersNestedDbRefCorrectly() { Update update = new Update().pull("nested.dbRefAnnotatedList.id", "2"); - DBObject mappedObject = mapper - .getMappedObject(update.getUpdateObject(), context.getPersistentEntity(Wrapper.class)); + DBObject mappedObject = mapper.getMappedObject(update.getUpdateObject(), + context.getPersistentEntity(Wrapper.class)); DBObject pullClause = getAsDBObject(mappedObject, "$pull"); assertThat(pullClause.containsField("mapped.dbRefAnnotatedList"), is(true)); @@ -660,8 +660,7 @@ public class UpdateMapperUnitTests { assertThat(mappedUpdate, isBsonObject().notContaining("$addToSet.listHoldingConcretyTypeWithInterfaceTypeAttribute.$each.[0]._class")); - assertThat( - mappedUpdate, + assertThat(mappedUpdate, isBsonObject().containing( "$addToSet.listHoldingConcretyTypeWithInterfaceTypeAttribute.$each.[0].interfaceType._class", ModelImpl.class.getName())); @@ -681,10 +680,8 @@ public class UpdateMapperUnitTests { 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())); } static class DomainTypeWrappingConcreteyTypeHavingListOfInterfaceTypeAttributes { @@ -735,7 +732,7 @@ public class UpdateMapperUnitTests { private @Id String id; - @org.springframework.data.mongodb.core.mapping.DBRef// + @org.springframework.data.mongodb.core.mapping.DBRef // private InterfaceDocumentDefinitionWithoutId referencedDocument; public String getId() { @@ -796,10 +793,10 @@ public class UpdateMapperUnitTests { String id; - @Field("aliased")// + @Field("aliased") // List list; - @Field// + @Field // List listOfInterface; public ParentClass(String id, List list) { @@ -861,10 +858,10 @@ public class UpdateMapperUnitTests { @Id public String id; - @org.springframework.data.mongodb.core.mapping.DBRef// + @org.springframework.data.mongodb.core.mapping.DBRef // public List dbRefAnnotatedList; - @org.springframework.data.mongodb.core.mapping.DBRef// + @org.springframework.data.mongodb.core.mapping.DBRef // public Entity dbRefProperty; } @@ -884,13 +881,12 @@ public class UpdateMapperUnitTests { } static class NestedDocument { + String name; public NestedDocument(String name) { super(); this.name = name; } - } - }