From 8ac2ff6b81ece69029d280e73a629a43fc9ecd46 Mon Sep 17 00:00:00 2001 From: Oliver Gierke Date: Thu, 9 Jun 2011 16:35:04 -0700 Subject: [PATCH] DATACMNS-169 - Revised custom converter handling. Registering custom converts now automatically declares the custom handled types simple by using the SimpleTypeHolder built up by the CustomConversions instance. All the custom converter lookup logic is now in CustomConversions and AbstractMongoConverter uses that over handling it itself. --- .../config/MappingMongoConverterParser.java | 80 ++++-- .../convert/AbstractMongoConverter.java | 115 ++------ .../mongodb/convert/CustomConversions.java | 260 ++++++++++++++++++ .../convert/MappingMongoConverter.java | 52 ++-- .../mapping/BasicMongoPersistentProperty.java | 7 +- .../mongodb/mapping/MongoMappingContext.java | 35 ++- .../mapping/SimpleMongoMappingContext.java | 9 +- .../mongodb/MongoOperationsUnitTests.java | 6 - .../mongodb/SimpleMongoConverterTests.java | 10 +- .../mongodb/TestMongoConfiguration.java | 15 +- .../convert/CustomConversionsUnitTests.java | 95 +++++++ .../convert/CustomConvertersUnitTests.java | 12 +- ...BasicMongoPersistentPropertyUnitTests.java | 3 +- .../MappingMongoConverterUnitTests.java | 20 +- .../src/test/resources/template-mapping.xml | 22 +- 15 files changed, 535 insertions(+), 206 deletions(-) create mode 100644 spring-data-mongodb/src/main/java/org/springframework/data/document/mongodb/convert/CustomConversions.java create mode 100644 spring-data-mongodb/src/test/java/org/springframework/data/document/mongodb/convert/CustomConversionsUnitTests.java diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/document/mongodb/config/MappingMongoConverterParser.java b/spring-data-mongodb/src/main/java/org/springframework/data/document/mongodb/config/MappingMongoConverterParser.java index 17e1800ab..8b6defefd 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/document/mongodb/config/MappingMongoConverterParser.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/document/mongodb/config/MappingMongoConverterParser.java @@ -30,6 +30,7 @@ import org.springframework.beans.factory.config.RuntimeBeanReference; import org.springframework.beans.factory.support.AbstractBeanDefinition; import org.springframework.beans.factory.support.BeanDefinitionBuilder; import org.springframework.beans.factory.support.BeanDefinitionRegistry; +import org.springframework.beans.factory.support.GenericBeanDefinition; import org.springframework.beans.factory.support.ManagedList; import org.springframework.beans.factory.support.ManagedSet; import org.springframework.beans.factory.xml.AbstractBeanDefinitionParser; @@ -37,6 +38,7 @@ import org.springframework.beans.factory.xml.ParserContext; import org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider; import org.springframework.core.type.filter.AnnotationTypeFilter; import org.springframework.data.annotation.Persistent; +import org.springframework.data.document.mongodb.convert.CustomConversions; import org.springframework.data.document.mongodb.convert.MappingMongoConverter; import org.springframework.data.document.mongodb.mapping.Document; import org.springframework.data.document.mongodb.mapping.MongoMappingContext; @@ -64,20 +66,9 @@ public class MappingMongoConverterParser extends AbstractBeanDefinitionParser { @Override protected AbstractBeanDefinition parseInternal(Element element, ParserContext parserContext) { BeanDefinitionRegistry registry = parserContext.getRegistry(); - - String ctxRef = element.getAttribute("mapping-context-ref"); - if (!StringUtils.hasText(ctxRef)) { - BeanDefinitionBuilder mappingContextBuilder = BeanDefinitionBuilder - .genericBeanDefinition(MongoMappingContext.class); - - Set classesToAdd = getInititalEntityClasses(element, mappingContextBuilder); - if (classesToAdd != null) { - mappingContextBuilder.addPropertyValue("initialEntitySet", classesToAdd); - } - - registry.registerBeanDefinition(MAPPING_CONTEXT, mappingContextBuilder.getBeanDefinition()); - ctxRef = MAPPING_CONTEXT; - } + + BeanDefinition conversionsDefinition = getCustomConversions(element, parserContext); + String ctxRef = potentiallyCreateMappingContext(element, parserContext, conversionsDefinition); try { registry.getBeanDefinition(POST_PROCESSOR); @@ -93,12 +84,15 @@ public class MappingMongoConverterParser extends AbstractBeanDefinitionParser { if (!StringUtils.hasText(dbFactoryRef)) { dbFactoryRef = DB_FACTORY; } - - + + // Converter BeanDefinitionBuilder converterBuilder = BeanDefinitionBuilder.genericBeanDefinition(MappingMongoConverter.class); converterBuilder.addConstructorArgReference(dbFactoryRef); converterBuilder.addConstructorArgReference(ctxRef); + if (conversionsDefinition != null) { + converterBuilder.addPropertyValue("customConversions", conversionsDefinition); + } try { registry.getBeanDefinition(INDEX_HELPER); @@ -112,20 +106,62 @@ public class MappingMongoConverterParser extends AbstractBeanDefinitionParser { registry.registerBeanDefinition(INDEX_HELPER, indexHelperBuilder.getBeanDefinition()); } + return converterBuilder.getBeanDefinition(); + } + + private String potentiallyCreateMappingContext(Element element, ParserContext parserContext, BeanDefinition conversionsDefinition) { + + String ctxRef = element.getAttribute("mapping-context-ref"); + if (!StringUtils.hasText(ctxRef)) { + BeanDefinitionBuilder mappingContextBuilder = BeanDefinitionBuilder + .genericBeanDefinition(MongoMappingContext.class); + + Set classesToAdd = getInititalEntityClasses(element, mappingContextBuilder); + if (classesToAdd != null) { + mappingContextBuilder.addPropertyValue("initialEntitySet", classesToAdd); + } + + if (conversionsDefinition != null) { + AbstractBeanDefinition simpleTypesDefinition = new GenericBeanDefinition(); + simpleTypesDefinition.setFactoryBeanName("customConversions"); + simpleTypesDefinition.setFactoryMethodName("getSimpleTypeHolder"); + + mappingContextBuilder.addPropertyValue("simpleTypeHolder", simpleTypesDefinition); + } + + parserContext.getRegistry().registerBeanDefinition(MAPPING_CONTEXT, mappingContextBuilder.getBeanDefinition()); + ctxRef = MAPPING_CONTEXT; + } + + return ctxRef; + } + + private BeanDefinition getCustomConversions(Element element, ParserContext parserContext) { + List customConvertersElements = DomUtils.getChildElementsByTagName(element, "custom-converters"); + if (customConvertersElements.size() == 1) { Element customerConvertersElement = customConvertersElements.get(0); ManagedList converterBeans = new ManagedList(); - List listenerElements = DomUtils.getChildElementsByTagName(customerConvertersElement, "converter"); - if (listenerElements != null) { - for (Element listenerElement : listenerElements) { + List converterElements = DomUtils.getChildElementsByTagName(customerConvertersElement, "converter"); + if (converterElements != null) { + for (Element listenerElement : converterElements) { converterBeans.add(parseConverter(listenerElement, parserContext)); } } - converterBuilder.addPropertyValue("customConverters", converterBeans); + + BeanDefinitionBuilder conversionsBuilder = BeanDefinitionBuilder.rootBeanDefinition(CustomConversions.class); + conversionsBuilder.addConstructorArgValue(converterBeans); + + AbstractBeanDefinition conversionsBean = conversionsBuilder.getBeanDefinition(); + conversionsBean.setSource(parserContext.extractSource(element)); + + parserContext.getRegistry().registerBeanDefinition("customConversions", conversionsBean); + + return conversionsBean; } - - return converterBuilder.getBeanDefinition(); + + return null; } public Set getInititalEntityClasses(Element element, BeanDefinitionBuilder builder) { diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/document/mongodb/convert/AbstractMongoConverter.java b/spring-data-mongodb/src/main/java/org/springframework/data/document/mongodb/convert/AbstractMongoConverter.java index 4919efbc7..7b3a8c5bb 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/document/mongodb/convert/AbstractMongoConverter.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/document/mongodb/convert/AbstractMongoConverter.java @@ -16,72 +16,55 @@ package org.springframework.data.document.mongodb.convert; -import static org.springframework.data.mapping.MappingBeanHelper.isSimpleType; - import java.math.BigInteger; import java.util.ArrayList; -import java.util.Arrays; -import java.util.Date; import java.util.HashMap; -import java.util.HashSet; import java.util.Iterator; import java.util.List; -import java.util.Locale; import java.util.Map; -import java.util.Set; - import org.bson.types.ObjectId; import org.springframework.beans.factory.InitializingBean; -import org.springframework.core.GenericTypeResolver; import org.springframework.core.convert.ConversionService; -import org.springframework.core.convert.TypeDescriptor; -import org.springframework.core.convert.converter.Converter; -import org.springframework.core.convert.converter.ConverterFactory; -import org.springframework.core.convert.converter.GenericConverter; -import org.springframework.core.convert.converter.GenericConverter.ConvertiblePair; import org.springframework.core.convert.support.ConversionServiceFactory; import org.springframework.core.convert.support.GenericConversionService; import org.springframework.data.document.mongodb.convert.ObjectIdConverters.BigIntegerToObjectIdConverter; import org.springframework.data.document.mongodb.convert.ObjectIdConverters.ObjectIdToBigIntegerConverter; import org.springframework.data.document.mongodb.convert.ObjectIdConverters.ObjectIdToStringConverter; import org.springframework.data.document.mongodb.convert.ObjectIdConverters.StringToObjectIdConverter; -import org.springframework.data.mapping.MappingBeanHelper; - import com.mongodb.BasicDBList; import com.mongodb.BasicDBObject; import com.mongodb.DBObject; /** + * Base class for {@link MongoConverter} implementations. Sets up a {@link GenericConversionService} and populates basic + * converters. Allows registering {@link CustomConversions}. + * * @author Jon Brisbin + * @author Oliver Gierke ogierke@vmware.com */ public abstract class AbstractMongoConverter implements MongoConverter, InitializingBean { - @SuppressWarnings({ "unchecked" }) - private static final List> MONGO_TYPES = Arrays.asList(Number.class, Date.class, String.class, - DBObject.class); - protected final GenericConversionService conversionService; - private final Set customTypeMapping = new HashSet(); + protected CustomConversions conversions = new CustomConversions(); + /** + * Creates a new {@link AbstractMongoConverter} using the given {@link GenericConversionService}. + * + * @param conversionService + */ public AbstractMongoConverter(GenericConversionService conversionService) { this.conversionService = conversionService == null ? ConversionServiceFactory.createDefaultConversionService() : conversionService; this.conversionService.removeConvertible(Object.class, String.class); - registerConverter(CustomToStringConverter.INSTANCE); } /** - * Add custom {@link Converter} or {@link ConverterFactory} instances to be used that will take presidence over - * metadata driven conversion between of objects to/from DBObject + * Registers the given custom conversions with the converter. * - * @param converters + * @param conversions */ - public void setCustomConverters(Set converters) { - if (null != converters) { - for (Object c : converters) { - registerConverter(c); - } - } + public void setCustomConversions(CustomConversions conversions) { + this.conversions = conversions; } /** @@ -102,61 +85,10 @@ public abstract class AbstractMongoConverter implements MongoConverter, Initiali if (!conversionService.canConvert(BigInteger.class, ObjectId.class)) { conversionService.addConverter(BigIntegerToObjectIdConverter.INSTANCE); } - } - - /** - * Inspects the given {@link Converter} for the types it can convert and registers the pair for custom type conversion - * in case the target type is a Mongo basic type. - * - * @param converter - */ - private void registerConverter(Object converter) { - if (converter instanceof GenericConverter) { - customTypeMapping.addAll(((GenericConverter) converter).getConvertibleTypes()); - } else { - Class[] arguments = GenericTypeResolver.resolveTypeArguments(converter.getClass(), Converter.class); - if (MONGO_TYPES.contains(arguments[1]) || MONGO_TYPES.contains(arguments[0])) { - customTypeMapping.add(new ConvertiblePair(arguments[0], arguments[1])); - } - } - - boolean added = false; - - if (converter instanceof Converter) { - this.conversionService.addConverter((Converter) converter); - added = true; - } - - if (converter instanceof ConverterFactory) { - this.conversionService.addConverterFactory((ConverterFactory) converter); - added = true; - } - - if (converter instanceof GenericConverter) { - this.conversionService.addConverter((GenericConverter) converter); - added = true; - } - - if (!added) { - throw new IllegalArgumentException("Given set contains element that is neither Converter nor ConverterFactory!"); - } + conversions.registerConvertersIn(conversionService); } - protected Class getCustomTarget(Class source, Class expectedTargetType) { - for (ConvertiblePair typePair : customTypeMapping) { - if (typePair.getSourceType().isAssignableFrom(source)) { - - Class targetType = typePair.getTargetType(); - - if (targetType.equals(expectedTargetType) || expectedTargetType == null) { - return targetType; - } - } - } - - return null; - } /* * (non-Javadoc) @@ -179,7 +111,7 @@ public abstract class AbstractMongoConverter implements MongoConverter, Initiali return ((Enum) obj).name(); } - if (null != obj && isSimpleType(obj.getClass())) { + if (null != obj && conversions.isSimpleType(obj.getClass())) { // Doesn't need conversion return obj; } @@ -240,19 +172,4 @@ public abstract class AbstractMongoConverter implements MongoConverter, Initiali } return newDbl; } - - - private enum CustomToStringConverter implements GenericConverter { - INSTANCE; - - public Set getConvertibleTypes() { - ConvertiblePair localeToString = new ConvertiblePair(Locale.class, String.class); - ConvertiblePair booleanToString = new ConvertiblePair(Character.class, String.class); - return new HashSet(Arrays.asList(localeToString, booleanToString)); - } - - public Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType) { - return source.toString(); - } - } } diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/document/mongodb/convert/CustomConversions.java b/spring-data-mongodb/src/main/java/org/springframework/data/document/mongodb/convert/CustomConversions.java new file mode 100644 index 000000000..6c8010857 --- /dev/null +++ b/spring-data-mongodb/src/main/java/org/springframework/data/document/mongodb/convert/CustomConversions.java @@ -0,0 +1,260 @@ +/* + * Copyright (c) 2011 by the original author(s). + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.data.document.mongodb.convert; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Date; +import java.util.HashSet; +import java.util.List; +import java.util.Locale; +import java.util.Set; + +import org.springframework.core.GenericTypeResolver; +import org.springframework.core.convert.TypeDescriptor; +import org.springframework.core.convert.converter.Converter; +import org.springframework.core.convert.converter.ConverterFactory; +import org.springframework.core.convert.converter.GenericConverter; +import org.springframework.core.convert.converter.GenericConverter.ConvertiblePair; +import org.springframework.core.convert.support.GenericConversionService; +import org.springframework.data.mapping.SimpleTypeHolder; +import org.springframework.util.Assert; + +import com.mongodb.DBObject; + +/** + * Value object to capture custom conversion. That is essentially a {@link List} of converters and some additional logic + * around them. The converters are pretty much builds up two sets of types which Mongo basic types {@see #MONGO_TYPES} + * can be converted into and from. These types will be considered simple ones (which means they neither need deeper + * inspection nor nested conversion. Thus the {@link CustomConversions} also act as factory for {@link SimpleTypeHolder}. + * + * @author Oliver Gierke + */ +public class CustomConversions { + + @SuppressWarnings({ "unchecked" }) + private static final List> MONGO_TYPES = Arrays.asList(Number.class, Date.class, String.class, + DBObject.class); + + private final Set readingPairs; + private final Set writingPairs; + private final Set> customSimpleTypes; + private final SimpleTypeHolder simpleTypeHolder; + + private final List converters; + + /** + * Creates an empty {@link CustomConversions} object. + */ + CustomConversions() { + this(new ArrayList()); + } + + /** + * Creates a new {@link CustomConversions} instance registering the given converters. + * + * @param converters + */ + public CustomConversions(List converters) { + + Assert.notNull(converters); + + this.readingPairs = new HashSet(); + this.writingPairs = new HashSet(); + this.customSimpleTypes = new HashSet>(); + + registerConversion(CustomToStringConverter.INSTANCE); + + for (Object c : converters) { + registerConversion(c); + } + + this.converters = new ArrayList(); + this.converters.add(CustomToStringConverter.INSTANCE); + this.converters.addAll(converters); + + this.simpleTypeHolder = new SimpleTypeHolder(customSimpleTypes, true); + } + + /** + * Returns the underlying {@link SimpleTypeHolder}. + * + * @return + */ + public SimpleTypeHolder getSimpleTypeHolder() { + return simpleTypeHolder; + } + + /** + * Returns whether the given type is considered to be simple. + * + * @param type + * @return + */ + public boolean isSimpleType(Class type) { + return simpleTypeHolder.isSimpleType(type); + } + + /** + * Populates the given {@link GenericConversionService} with the convertes registered. + * + * @param conversionService + */ + public void registerConvertersIn(GenericConversionService conversionService) { + + for (Object converter : converters) { + + boolean added = false; + + if (converter instanceof Converter) { + conversionService.addConverter((Converter) converter); + added = true; + } + + if (converter instanceof ConverterFactory) { + conversionService.addConverterFactory((ConverterFactory) converter); + added = true; + } + + if (converter instanceof GenericConverter) { + conversionService.addConverter((GenericConverter) converter); + added = true; + } + + if (!added) { + throw new IllegalArgumentException("Given set contains element that is neither Converter nor ConverterFactory!"); + } + } + } + + /** + * Registers a conversion for the given converter. Inspects either generics or the {@link ConvertiblePair}s returned + * by a {@link GenericConverter}. + * + * @param converter + */ + private void registerConversion(Object converter) { + + if (converter instanceof GenericConverter) { + GenericConverter genericConverter = (GenericConverter) converter; + for (ConvertiblePair pair : genericConverter.getConvertibleTypes()) { + register(pair); + } + } else if (converter instanceof Converter){ + Class[] arguments = GenericTypeResolver.resolveTypeArguments(converter.getClass(), Converter.class); + register(new ConvertiblePair(arguments[0], arguments[1])); + } else { + throw new IllegalArgumentException("Unsupported Converter type!"); + } + } + + /** + * Registers the given {@link ConvertiblePair} as reading or writing pair depending on the type sides being basic + * Mongo types. + * + * @param pair + */ + private void register(ConvertiblePair pair) { + + if (isMongoBasicType(pair.getSourceType())) { + readingPairs.add(pair); + customSimpleTypes.add(pair.getTargetType()); + } + + if (isMongoBasicType(pair.getTargetType())) { + writingPairs.add(pair); + customSimpleTypes.add(pair.getSourceType()); + } + } + + /** + * Returns the target type we can write an onject of the given source type to. The returned type might be a subclass + * oth the given expected type though. If {@code expexctedTargetType} is {@literal null} we will simply return the + * first target type matching or {@literal null} if noe conversion can be found. + * + * @param source must not be {@literal null} + * @param expectedTargetType + * @return + */ + public Class getCustomWriteTarget(Class source, Class expectedTargetType) { + Assert.notNull(source); + return getCustomTarget(source, expectedTargetType, writingPairs); + } + + /** + * Returns whether we have a custom conversion registered to read the given source into the given target type. + * + * @param source must not be {@literal null} + * @param expectedTargetType must not be {@literal null} + * @return + */ + public boolean hasCustomReadTarget(Class source, Class expectedTargetType) { + Assert.notNull(source); + Assert.notNull(expectedTargetType); + return getCustomTarget(source, expectedTargetType, readingPairs) != null; + } + + /** + * Inspects the given {@link ConvertiblePair} for ones that have a source compatible type as source. Additionally + * checks assignabilty of the target type if one is given. + * + * @param source must not be {@literal null} + * @param expectedTargetType + * @param pairs must not be {@literal null} + * @return + */ + private static Class getCustomTarget(Class source, Class expectedTargetType, Iterable pairs) { + + Assert.notNull(source); + Assert.notNull(pairs); + + for (ConvertiblePair typePair : pairs) { + if (typePair.getSourceType().isAssignableFrom(source)) { + Class targetType = typePair.getTargetType(); + if (expectedTargetType == null || targetType.isAssignableFrom(expectedTargetType)) { + return targetType; + } + } + } + + return null; + } + + /** + * Returns whether the given type is a type that Mongo can handle basically. + * + * @param type + * @return + */ + private static boolean isMongoBasicType(Class type) { + return MONGO_TYPES.contains(type); + } + + + private enum CustomToStringConverter implements GenericConverter { + INSTANCE; + + public Set getConvertibleTypes() { + ConvertiblePair localeToString = new ConvertiblePair(Locale.class, String.class); + ConvertiblePair booleanToString = new ConvertiblePair(Character.class, String.class); + return new HashSet(Arrays.asList(localeToString, booleanToString)); + } + + public Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType) { + return source.toString(); + } + } +} diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/document/mongodb/convert/MappingMongoConverter.java b/spring-data-mongodb/src/main/java/org/springframework/data/document/mongodb/convert/MappingMongoConverter.java index bbe3c4e3d..c2c9a0a0c 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/document/mongodb/convert/MappingMongoConverter.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/document/mongodb/convert/MappingMongoConverter.java @@ -16,8 +16,6 @@ package org.springframework.data.document.mongodb.convert; -import static org.springframework.data.mapping.MappingBeanHelper.*; - import java.lang.reflect.Array; import java.lang.reflect.InvocationTargetException; import java.math.BigInteger; @@ -138,9 +136,8 @@ public class MappingMongoConverter extends AbstractMongoConverter implements App TypeInformation typeToUse = getMoreConcreteTargetType(dbo, type); Class rawType = typeToUse.getType(); - Class customTarget = getCustomTarget(rawType, DBObject.class); - if (customTarget != null) { + if (conversions.hasCustomReadTarget(DBObject.class, rawType)) { return conversionService.convert(dbo, rawType); } @@ -279,7 +276,7 @@ public class MappingMongoConverter extends AbstractMongoConverter implements App return; } - boolean handledByCustomConverter = getCustomTarget(obj.getClass(), DBObject.class) != null; + boolean handledByCustomConverter = conversions.getCustomWriteTarget(obj.getClass(), DBObject.class) != null; if (!handledByCustomConverter) { dbo.put(CUSTOM_TYPE_KEY, obj.getClass().getName()); @@ -301,7 +298,7 @@ public class MappingMongoConverter extends AbstractMongoConverter implements App return; } - Class customTarget = getCustomTarget(obj.getClass(), DBObject.class); + Class customTarget = conversions.getCustomWriteTarget(obj.getClass(), DBObject.class); if (customTarget != null) { DBObject result = conversionService.convert(obj, DBObject.class); @@ -374,7 +371,7 @@ public class MappingMongoConverter extends AbstractMongoConverter implements App throw new MappingException(e.getMessage(), e); } if (null != propertyObj) { - if (!isSimpleType(propertyObj.getClass())) { + if (!conversions.isSimpleType(propertyObj.getClass())) { writePropertyInternal(prop, propertyObj, dbo); } else { writeSimpleInternal(prop.getFieldName(), propertyObj, dbo); @@ -437,7 +434,7 @@ public class MappingMongoConverter extends AbstractMongoConverter implements App } // Lookup potential custom target type - Class basicTargetType = getCustomTarget(obj.getClass(), null); + Class basicTargetType = conversions.getCustomWriteTarget(obj.getClass(), null); if (basicTargetType != null) { dbo.put(name, conversionService.convert(obj, basicTargetType)); @@ -516,7 +513,7 @@ public class MappingMongoConverter extends AbstractMongoConverter implements App Class elementType = element.getClass(); - if (isSimpleType(elementType)) { + if (conversions.isSimpleType(elementType)) { dbList.add(element); } else if (element instanceof Collection || elementType.isArray()) { dbList.add(createCollectionDBObject(componentType, asCollection(element))); @@ -535,11 +532,11 @@ public class MappingMongoConverter extends AbstractMongoConverter implements App for (Map.Entry entry : obj.entrySet()) { Object key = entry.getKey(); Object val = entry.getValue(); - if (isSimpleType(key.getClass())) { + if (conversions.isSimpleType(key.getClass())) { // Don't use conversion service here as removal of ObjectToString converter results in some primitive types not // being convertable String simpleKey = key.toString(); - if (isSimpleType(val.getClass())) { + if (conversions.isSimpleType(val.getClass())) { writeSimpleInternal(simpleKey, val, dbo); } else { DBObject newDbo = new BasicDBObject(); @@ -602,7 +599,7 @@ public class MappingMongoConverter extends AbstractMongoConverter implements App */ private void writeSimpleInternal(String key, Object value, DBObject dbObject) { - Class customTarget = getCustomTarget(value.getClass(), null); + Class customTarget = conversions.getCustomWriteTarget(value.getClass(), null); Object valueToSet = null; if (customTarget != null) { @@ -652,30 +649,29 @@ public class MappingMongoConverter extends AbstractMongoConverter implements App o = x.getValue(ctx); } else { - Object dbObj = dbo.get(prop.getFieldName()); + Object sourceValue = dbo.get(prop.getFieldName()); - if (dbObj == null) { + if (sourceValue == null) { return null; } Class propertyType = prop.getType(); - Class customTarget = getCustomTarget(dbObj.getClass(), propertyType); - if (customTarget != null) { - return conversionService.convert(dbObj, propertyType); + if (conversions.hasCustomReadTarget(sourceValue.getClass(), propertyType)) { + return conversionService.convert(sourceValue, propertyType); } - if (dbObj instanceof DBRef) { - dbObj = ((DBRef) dbObj).fetch(); + if (sourceValue instanceof DBRef) { + sourceValue = ((DBRef) sourceValue).fetch(); } - if (dbObj instanceof DBObject) { + if (sourceValue instanceof DBObject) { if (prop.isMap()) { - return readMap(prop.getTypeInformation(), (DBObject) dbObj); - } else if (prop.isArray() && dbObj instanceof BasicDBObject && ((DBObject) dbObj).keySet().size() == 0) { + return readMap(prop.getTypeInformation(), (DBObject) sourceValue); + } else if (prop.isArray() && sourceValue instanceof BasicDBObject && ((DBObject) sourceValue).keySet().size() == 0) { // It's empty return Array.newInstance(prop.getComponentType(), 0); - } else if (prop.isCollection() && dbObj instanceof BasicDBList) { - BasicDBList dbObjList = (BasicDBList) dbObj; + } else if (prop.isCollection() && sourceValue instanceof BasicDBList) { + BasicDBList dbObjList = (BasicDBList) sourceValue; List items = new LinkedList(); for (int i = 0; i < dbObjList.size(); i++) { Object dbObjItem = dbObjList.get(i); @@ -694,17 +690,17 @@ public class MappingMongoConverter extends AbstractMongoConverter implements App return itemsToReturn; } - Class toType = findTypeToBeUsed((DBObject) dbObj); + Class toType = findTypeToBeUsed((DBObject) sourceValue); // It's a complex object, have to read it in if (toType != null) { dbo.removeField(CUSTOM_TYPE_KEY); - o = read(toType, (DBObject) dbObj); + o = read(toType, (DBObject) sourceValue); } else { - o = read(mappingContext.getPersistentEntity(prop.getTypeInformation()), (DBObject) dbObj); + o = read(mappingContext.getPersistentEntity(prop.getTypeInformation()), (DBObject) sourceValue); } } else { - o = dbObj; + o = sourceValue; } } return o; diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/document/mongodb/mapping/BasicMongoPersistentProperty.java b/spring-data-mongodb/src/main/java/org/springframework/data/document/mongodb/mapping/BasicMongoPersistentProperty.java index b9c1ba8ea..039691121 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/document/mongodb/mapping/BasicMongoPersistentProperty.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/document/mongodb/mapping/BasicMongoPersistentProperty.java @@ -26,6 +26,7 @@ import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.bson.types.ObjectId; import org.springframework.data.mapping.AnnotationBasedPersistentProperty; +import org.springframework.data.mapping.SimpleTypeHolder; import org.springframework.data.mapping.model.Association; /** @@ -56,9 +57,11 @@ public class BasicMongoPersistentProperty extends AnnotationBasedPersistentPrope * @param field * @param propertyDescriptor * @param owner + * @param simpleTypeHolder */ - public BasicMongoPersistentProperty(Field field, PropertyDescriptor propertyDescriptor, MongoPersistentEntity owner) { - super(field, propertyDescriptor, owner); + public BasicMongoPersistentProperty(Field field, PropertyDescriptor propertyDescriptor, + MongoPersistentEntity owner, SimpleTypeHolder simpleTypeHolder) { + super(field, propertyDescriptor, owner, simpleTypeHolder); if (isIdProperty() && field.isAnnotationPresent(FieldName.class)) { LOG.warn(String.format("Invalid usage of %s on id property. Field name will not be considered!", FieldName.class)); diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/document/mongodb/mapping/MongoMappingContext.java b/spring-data-mongodb/src/main/java/org/springframework/data/document/mongodb/mapping/MongoMappingContext.java index 6caf07b57..3a2820156 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/document/mongodb/mapping/MongoMappingContext.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/document/mongodb/mapping/MongoMappingContext.java @@ -18,12 +18,13 @@ package org.springframework.data.document.mongodb.mapping; import java.beans.PropertyDescriptor; import java.lang.reflect.Field; +import java.util.HashSet; import java.util.Set; import org.bson.types.CodeWScope; import org.bson.types.ObjectId; import org.springframework.data.mapping.AbstractMappingContext; -import org.springframework.data.mapping.MappingBeanHelper; +import org.springframework.data.mapping.SimpleTypeHolder; import org.springframework.data.util.TypeInformation; /** @@ -31,23 +32,33 @@ import org.springframework.data.util.TypeInformation; * @author Oliver Gierke ogierke@vmware.com */ public class MongoMappingContext extends AbstractMappingContext, MongoPersistentProperty> { - public MongoMappingContext() { - augmentSimpleTypes(); + + private static final Set> MONGO_SIMPLE_TYPES = new HashSet>(); + + static { + MONGO_SIMPLE_TYPES.add(com.mongodb.DBRef.class); + MONGO_SIMPLE_TYPES.add(ObjectId.class); + MONGO_SIMPLE_TYPES.add(CodeWScope.class); + MONGO_SIMPLE_TYPES.add(Character.class); } - protected void augmentSimpleTypes() { - // Augment simpleTypes with MongoDB-specific classes - Set> simpleTypes = MappingBeanHelper.getSimpleTypes(); - simpleTypes.add(com.mongodb.DBRef.class); - simpleTypes.add(ObjectId.class); - simpleTypes.add(CodeWScope.class); - simpleTypes.add(Character.class); + /* + * (non-Javadoc) + * @see org.springframework.data.mapping.AbstractMappingContext#setSimpleTypeHolder(org.springframework.data.mapping.SimpleTypeHolder) + */ + @Override + public void setSimpleTypeHolder(SimpleTypeHolder simpleTypes) { + super.setSimpleTypeHolder(new SimpleTypeHolder(MONGO_SIMPLE_TYPES, simpleTypes)); } + /* + * (non-Javadoc) + * @see org.springframework.data.mapping.AbstractMappingContext#createPersistentProperty(java.lang.reflect.Field, java.beans.PropertyDescriptor, org.springframework.data.mapping.MutablePersistentEntity, org.springframework.data.mapping.SimpleTypeHolder) + */ @Override public MongoPersistentProperty createPersistentProperty(Field field, PropertyDescriptor descriptor, - BasicMongoPersistentEntity owner) { - return new BasicMongoPersistentProperty(field, descriptor, owner); + BasicMongoPersistentEntity owner, SimpleTypeHolder simpleTypeHolder) { + return new BasicMongoPersistentProperty(field, descriptor, owner, simpleTypeHolder); } /* (non-Javadoc) diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/document/mongodb/mapping/SimpleMongoMappingContext.java b/spring-data-mongodb/src/main/java/org/springframework/data/document/mongodb/mapping/SimpleMongoMappingContext.java index 474cb6509..c8eb58717 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/document/mongodb/mapping/SimpleMongoMappingContext.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/document/mongodb/mapping/SimpleMongoMappingContext.java @@ -24,6 +24,7 @@ import org.springframework.data.document.mongodb.MongoCollectionUtils; import org.springframework.data.mapping.AbstractMappingContext; import org.springframework.data.mapping.BasicPersistentEntity; import org.springframework.data.mapping.AbstractPersistentProperty; +import org.springframework.data.mapping.SimpleTypeHolder; import org.springframework.data.mapping.model.Association; import org.springframework.data.util.TypeInformation; @@ -47,8 +48,8 @@ public class SimpleMongoMappingContext extends */ @Override protected SimplePersistentProperty createPersistentProperty(Field field, PropertyDescriptor descriptor, - SimpleMongoPersistentEntity owner) { - return new SimplePersistentProperty(field, descriptor, owner); + SimpleMongoPersistentEntity owner, SimpleTypeHolder simpleTypeHolder) { + return new SimplePersistentProperty(field, descriptor, owner, simpleTypeHolder); } static class SimplePersistentProperty extends AbstractPersistentProperty implements @@ -63,8 +64,8 @@ public class SimpleMongoMappingContext extends * @param propertyDescriptor * @param information */ - public SimplePersistentProperty(Field field, PropertyDescriptor propertyDescriptor, MongoPersistentEntity owner) { - super(field, propertyDescriptor, owner); + public SimplePersistentProperty(Field field, PropertyDescriptor propertyDescriptor, MongoPersistentEntity owner, SimpleTypeHolder simpleTypeHolder) { + super(field, propertyDescriptor, owner, simpleTypeHolder); } /* (non-Javadoc) diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/document/mongodb/MongoOperationsUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/document/mongodb/MongoOperationsUnitTests.java index 6ec056a61..4815e1be2 100644 --- a/spring-data-mongodb/src/test/java/org/springframework/data/document/mongodb/MongoOperationsUnitTests.java +++ b/spring-data-mongodb/src/test/java/org/springframework/data/document/mongodb/MongoOperationsUnitTests.java @@ -20,8 +20,6 @@ import static org.junit.Assert.*; import java.util.Arrays; import java.util.List; -import java.util.Set; - import com.mongodb.BasicDBObject; import com.mongodb.DBObject; import org.bson.types.ObjectId; @@ -83,10 +81,6 @@ public abstract class MongoOperationsUnitTests { public MappingContext, MongoPersistentProperty> getMappingContext() { return null; } - - @Override - public void setCustomConverters(Set converters) { - } }; } diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/document/mongodb/SimpleMongoConverterTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/document/mongodb/SimpleMongoConverterTests.java index 10cc6cd8b..9aad7bb3e 100644 --- a/spring-data-mongodb/src/test/java/org/springframework/data/document/mongodb/SimpleMongoConverterTests.java +++ b/spring-data-mongodb/src/test/java/org/springframework/data/document/mongodb/SimpleMongoConverterTests.java @@ -29,11 +29,8 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.Date; import java.util.HashMap; -import java.util.HashSet; import java.util.List; import java.util.Map; -import java.util.Set; - import org.hamcrest.CoreMatchers; import org.joda.time.LocalDate; import org.junit.Before; @@ -41,6 +38,7 @@ import org.junit.Test; import org.springframework.core.convert.converter.Converter; import org.springframework.data.document.mongodb.SomeEnumTest.NumberEnum; import org.springframework.data.document.mongodb.SomeEnumTest.StringEnum; +import org.springframework.data.document.mongodb.convert.CustomConversions; import org.springframework.data.document.mongodb.convert.SimpleMongoConverter; import org.springframework.util.ReflectionUtils; @@ -48,6 +46,7 @@ import com.mongodb.BasicDBObject; import com.mongodb.DBObject; import com.mongodb.util.JSON; +@SuppressWarnings("deprecation") public class SimpleMongoConverterTests { static final String SIMPLE_JSON = "{ \"map\" : { \"foo\" : 3 , \"bar\" : 4}, \"number\" : 15 }"; @@ -329,11 +328,12 @@ public class SimpleMongoConverterTests { @Test public void convertsJodaTimeTypesCorrectly() { - Set> converters = new HashSet>(); + List> converters = new ArrayList>(); converters.add(new LocalDateToDateConverter()); converters.add(new DateToLocalDateConverter()); - converter.setCustomConverters(converters); + converter.setCustomConversions(new CustomConversions(converters)); + converter.afterPropertiesSet(); AnotherPerson person = new AnotherPerson(); person.birthDate = new LocalDate(); diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/document/mongodb/TestMongoConfiguration.java b/spring-data-mongodb/src/test/java/org/springframework/data/document/mongodb/TestMongoConfiguration.java index a48b14bc4..8ac9acf52 100644 --- a/spring-data-mongodb/src/test/java/org/springframework/data/document/mongodb/TestMongoConfiguration.java +++ b/spring-data-mongodb/src/test/java/org/springframework/data/document/mongodb/TestMongoConfiguration.java @@ -1,12 +1,12 @@ package org.springframework.data.document.mongodb; -import java.util.HashSet; -import java.util.Set; - +import java.util.ArrayList; +import java.util.List; import com.mongodb.Mongo; import org.springframework.context.annotation.Bean; import org.springframework.core.convert.converter.Converter; import org.springframework.data.document.mongodb.config.AbstractMongoConfiguration; +import org.springframework.data.document.mongodb.convert.CustomConversions; import org.springframework.data.document.mongodb.convert.MappingMongoConverter; public class TestMongoConfiguration extends AbstractMongoConfiguration { @@ -16,6 +16,7 @@ public class TestMongoConfiguration extends AbstractMongoConfiguration { return "database"; } + @Override @Bean public Mongo mongo() throws Exception { return new Mongo("localhost", 27017); @@ -28,10 +29,10 @@ public class TestMongoConfiguration extends AbstractMongoConfiguration { @Override protected void afterMappingMongoConverterCreation(MappingMongoConverter converter) { - Set> converterList = new HashSet>(); - converterList.add(new org.springframework.data.document.mongodb.PersonReadConverter()); - converterList.add(new org.springframework.data.document.mongodb.PersonWriteConverter()); - converter.setCustomConverters(converterList); + List> converters = new ArrayList>(); + converters.add(new org.springframework.data.document.mongodb.PersonReadConverter()); + converters.add(new org.springframework.data.document.mongodb.PersonWriteConverter()); + converter.setCustomConversions(new CustomConversions(converters)); } diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/document/mongodb/convert/CustomConversionsUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/document/mongodb/convert/CustomConversionsUnitTests.java new file mode 100644 index 000000000..1cb424a8b --- /dev/null +++ b/spring-data-mongodb/src/test/java/org/springframework/data/document/mongodb/convert/CustomConversionsUnitTests.java @@ -0,0 +1,95 @@ +package org.springframework.data.document.mongodb.convert; + +import static org.junit.Assert.*; +import static org.hamcrest.Matchers.*; + +import java.util.Arrays; +import java.util.Locale; +import java.util.UUID; + +import org.junit.Test; +import org.springframework.core.convert.converter.Converter; +import org.springframework.core.convert.support.ConversionServiceFactory; +import org.springframework.core.convert.support.GenericConversionService; + +/** + * Unit tests for {@link CustomConversions}. + * + * @author Oliver Gierke + */ +public class CustomConversionsUnitTests { + + @Test + @SuppressWarnings("unchecked") + public void findsBasicReadAndWriteConversions() { + + CustomConversions conversions = new CustomConversions(Arrays.asList(UuidToStringConverter.INSTANCE, + StringToUUIDConverter.INSTANCE)); + + assertThat(conversions.getCustomWriteTarget(UUID.class, null), is(typeCompatibleWith(String.class))); + assertThat(conversions.getCustomWriteTarget(String.class, null), is(nullValue())); + + assertThat(conversions.hasCustomReadTarget(String.class, UUID.class), is(true)); + assertThat(conversions.hasCustomReadTarget(String.class, Locale.class), is(false)); + } + + @Test + @SuppressWarnings("unchecked") + public void considersSubtypesCorrectly() { + + CustomConversions conversions = new CustomConversions(Arrays.asList( + NumberToStringConverter.INSTANCE, StringToNumberConverter.INSTANCE)); + + assertThat(conversions.getCustomWriteTarget(Long.class, null), is(typeCompatibleWith(String.class))); + assertThat(conversions.hasCustomReadTarget(String.class, Long.class), is(true)); + } + + @Test + public void considersTypesWeRegisteredConvertersForAsSimple() { + + CustomConversions conversions = new CustomConversions( Arrays.asList(UuidToStringConverter.INSTANCE)); + assertThat(conversions.isSimpleType(UUID.class), is(true)); + } + + @Test + public void populatesConversionServiceCorrectly() { + + GenericConversionService conversionService = ConversionServiceFactory.createDefaultConversionService(); + assertThat(conversionService.canConvert(String.class, UUID.class), is(false)); + + CustomConversions conversions = new CustomConversions( + Arrays.asList(StringToUUIDConverter.INSTANCE)); + conversions.registerConvertersIn(conversionService); + + assertThat(conversionService.canConvert(String.class, UUID.class), is(true)); + } + + enum UuidToStringConverter implements Converter { + INSTANCE; + + public String convert(UUID source) { + return source.toString(); + } + } + + enum StringToUUIDConverter implements Converter { + INSTANCE; + public UUID convert(String source) { + return UUID.fromString(source); + } + } + + enum NumberToStringConverter implements Converter { + INSTANCE; + public String convert(Number source) { + return source.toString(); + } + } + + enum StringToNumberConverter implements Converter { + INSTANCE; + public Number convert(String source) { + return 0L; + } + } +} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/document/mongodb/convert/CustomConvertersUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/document/mongodb/convert/CustomConvertersUnitTests.java index f68a93a06..483fe9f20 100644 --- a/spring-data-mongodb/src/test/java/org/springframework/data/document/mongodb/convert/CustomConvertersUnitTests.java +++ b/spring-data-mongodb/src/test/java/org/springframework/data/document/mongodb/convert/CustomConvertersUnitTests.java @@ -61,15 +61,19 @@ public class CustomConvertersUnitTests { @SuppressWarnings("unchecked") public void setUp() throws Exception { + when(barToDBObjectConverter.convert(any(Bar.class))).thenReturn(new BasicDBObject()); + when(dbObjectToBarConverter.convert(any(DBObject.class))).thenReturn(new Bar()); + + CustomConversions conversions = new CustomConversions(Arrays.asList(barToDBObjectConverter, dbObjectToBarConverter)); + context = new MongoMappingContext(); context.setInitialEntitySet(new HashSet>(Arrays.asList(Foo.class, Bar.class))); + context.setSimpleTypeHolder(conversions.getSimpleTypeHolder()); context.afterPropertiesSet(); - when(barToDBObjectConverter.convert(any(Bar.class))).thenReturn(new BasicDBObject()); - when(dbObjectToBarConverter.convert(any(DBObject.class))).thenReturn(new Bar()); - converter = new MappingMongoConverter(mongoDbFactory, context); - converter.setCustomConverters(new HashSet(Arrays.asList(barToDBObjectConverter, dbObjectToBarConverter))); + converter.setCustomConversions(conversions); + converter.afterPropertiesSet(); } @Test diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/document/mongodb/mapping/BasicMongoPersistentPropertyUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/document/mongodb/mapping/BasicMongoPersistentPropertyUnitTests.java index be1e0d0a5..7b9a6b020 100644 --- a/spring-data-mongodb/src/test/java/org/springframework/data/document/mongodb/mapping/BasicMongoPersistentPropertyUnitTests.java +++ b/spring-data-mongodb/src/test/java/org/springframework/data/document/mongodb/mapping/BasicMongoPersistentPropertyUnitTests.java @@ -22,6 +22,7 @@ import java.lang.reflect.Field; import org.junit.Before; import org.junit.Test; import org.springframework.data.annotation.Id; +import org.springframework.data.mapping.SimpleTypeHolder; import org.springframework.data.util.ClassTypeInformation; import org.springframework.util.ReflectionUtils; @@ -62,7 +63,7 @@ public class BasicMongoPersistentPropertyUnitTests { } private MongoPersistentProperty getPropertyFor(Field field) { - return new BasicMongoPersistentProperty(field, null, entity); + return new BasicMongoPersistentProperty(field, null, entity, new SimpleTypeHolder()); } class Person { diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/document/mongodb/mapping/MappingMongoConverterUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/document/mongodb/mapping/MappingMongoConverterUnitTests.java index 26c4452d2..7b86b5c9e 100644 --- a/spring-data-mongodb/src/test/java/org/springframework/data/document/mongodb/mapping/MappingMongoConverterUnitTests.java +++ b/spring-data-mongodb/src/test/java/org/springframework/data/document/mongodb/mapping/MappingMongoConverterUnitTests.java @@ -25,13 +25,10 @@ import java.util.Arrays; import java.util.Collections; import java.util.Date; import java.util.HashMap; -import java.util.HashSet; import java.util.LinkedHashMap; import java.util.List; import java.util.Locale; import java.util.Map; -import java.util.Set; - import org.joda.time.LocalDate; import org.junit.Before; import org.junit.Test; @@ -40,6 +37,7 @@ import org.mockito.Mock; import org.mockito.runners.MockitoJUnitRunner; import org.springframework.core.convert.converter.Converter; import org.springframework.data.document.mongodb.MongoDbFactory; +import org.springframework.data.document.mongodb.convert.CustomConversions; import org.springframework.data.document.mongodb.convert.MappingMongoConverter; import com.mongodb.BasicDBList; @@ -61,8 +59,12 @@ public class MappingMongoConverterUnitTests { @Before public void setUp() { + mappingContext = new MongoMappingContext(); + mappingContext.afterPropertiesSet(); + converter = new MappingMongoConverter(factory, mappingContext); + converter.afterPropertiesSet(); } @Test @@ -83,16 +85,15 @@ public class MappingMongoConverterUnitTests { @Test public void convertsJodaTimeTypesCorrectly() { - Set> converters = new HashSet>(); + List> converters = new ArrayList>(); converters.add(new LocalDateToDateConverter()); converters.add(new DateToLocalDateConverter()); - - List> customSimpleTypes = new ArrayList>(); - customSimpleTypes.add(LocalDate.class); - mappingContext.setCustomSimpleTypes(customSimpleTypes); + + CustomConversions conversions = new CustomConversions(converters); + mappingContext.setSimpleTypeHolder(conversions.getSimpleTypeHolder()); converter = new MappingMongoConverter(factory, mappingContext); - converter.setCustomConverters(converters); + converter.setCustomConversions(conversions); converter.afterPropertiesSet(); Person person = new Person(); @@ -343,6 +344,7 @@ public class MappingMongoConverterUnitTests { } @Test + @SuppressWarnings("unchecked") public void writesNestedCollectionsCorrectly() { CollectionWrapper wrapper = new CollectionWrapper(); diff --git a/spring-data-mongodb/src/test/resources/template-mapping.xml b/spring-data-mongodb/src/test/resources/template-mapping.xml index de2726a28..d201f40cc 100644 --- a/spring-data-mongodb/src/test/resources/template-mapping.xml +++ b/spring-data-mongodb/src/test/resources/template-mapping.xml @@ -12,15 +12,23 @@ - - - - - - + - + + + + + + + + + + + + + +