From eb276841ddd56598851acf6fa2fed4cbed31ace2 Mon Sep 17 00:00:00 2001 From: Oliver Gierke Date: Thu, 28 Jul 2011 19:32:33 +0200 Subject: [PATCH] DATADOC-214 - Cleaned up MongoConverter interface and implementations. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Removed quite some obsolete methods from MongoConverter interface. Renamed maybeConvertObject(…) to convertToMongoType(…). Moved implementation of that method into MappingMongoConverter. Let the implementation transparently use custom Converters as well. Removed SimpleMongoConverter. Switched QueryMapper implementation from using a MongoConverter to use a ConversionService. Removed custom "maybe convert" logic from ConvertingParameterAccessor in favor of MongoWriter.convertToMongo(…). --- .../data/mongodb/core/MongoTemplate.java | 8 +- .../data/mongodb/core/QueryMapper.java | 22 +- .../core/convert/AbstractMongoConverter.java | 83 --- .../core/convert/CustomConversions.java | 98 ++-- .../core/convert/MappingMongoConverter.java | 92 +++- .../mongodb/core/convert/MongoConverter.java | 38 +- .../mongodb/core/convert/MongoWriter.java | 14 +- .../core/convert/SimpleMongoConverter.java | 516 ------------------ .../ConvertingParameterAccessor.java | 36 +- .../core/MongoOperationsUnitTests.java | 9 +- .../data/mongodb/core/MongoTemplateTests.java | 32 +- .../core/SimpleMongoConverterTests.java | 439 --------------- .../MappingMongoConverterUnitTests.java | 8 +- .../core/query/QueryMapperUnitTests.java | 6 +- 14 files changed, 208 insertions(+), 1193 deletions(-) delete mode 100644 spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/SimpleMongoConverter.java delete mode 100644 spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/SimpleMongoConverterTests.java diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/MongoTemplate.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/MongoTemplate.java index 858f07882..638cb8c72 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/MongoTemplate.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/MongoTemplate.java @@ -168,7 +168,7 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware { this.mongoDbFactory = mongoDbFactory; this.mongoConverter = mongoConverter == null ? getDefaultMongoConverter(mongoDbFactory) : mongoConverter; - this.mapper = new QueryMapper(this.mongoConverter); + this.mapper = new QueryMapper(this.mongoConverter.getConversionService()); // We always have a mapping context in the converter, whether it's a simple one or not mappingContext = this.mongoConverter.getMappingContext(); @@ -686,15 +686,15 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware { for (String key : queryObj.keySet()) { if (idProperty.equals(key)) { // This is an ID field - queryObj.put(ID, mongoConverter.maybeConvertObject(queryObj.get(key))); + queryObj.put(ID, mongoConverter.convertToMongoType(queryObj.get(key))); queryObj.removeField(key); } else { - queryObj.put(key, mongoConverter.maybeConvertObject(queryObj.get(key))); + queryObj.put(key, mongoConverter.convertToMongoType(queryObj.get(key))); } } for (String key : updateObj.keySet()) { - updateObj.put(key, mongoConverter.maybeConvertObject(updateObj.get(key))); + updateObj.put(key, mongoConverter.convertToMongoType(updateObj.get(key))); } if (LOGGER.isDebugEnabled()) { diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/QueryMapper.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/QueryMapper.java index cef92000f..c7def316e 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/QueryMapper.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/QueryMapper.java @@ -24,8 +24,8 @@ import com.mongodb.DBObject; import org.bson.types.BasicBSONList; import org.bson.types.ObjectId; import org.springframework.core.convert.ConversionException; +import org.springframework.core.convert.ConversionService; import org.springframework.data.mapping.PersistentEntity; -import org.springframework.data.mongodb.core.convert.MongoConverter; import org.springframework.data.mongodb.core.mapping.MongoPersistentEntity; import org.springframework.util.Assert; @@ -37,16 +37,16 @@ import org.springframework.util.Assert; */ public class QueryMapper { - private final MongoConverter converter; + private final ConversionService conversionService; /** - * Creates a new {@link QueryMapper} with the given {@link MongoConverter}. + * Creates a new {@link QueryMapper} with the given {@link ConversionService}. * - * @param converter + * @param conversionService must not be {@literal null}. */ - public QueryMapper(MongoConverter converter) { - Assert.notNull(converter); - this.converter = converter; + public QueryMapper(ConversionService conversionService) { + Assert.notNull(conversionService); + this.conversionService = conversionService; } /** @@ -78,9 +78,9 @@ public class QueryMapper { String inKey = valueDbo.containsField("$in") ? "$in" : "$nin"; List ids = new ArrayList(); for (Object id : (Object[]) valueDbo.get(inKey)) { - if (null != converter && !(id instanceof ObjectId)) { + if (!(id instanceof ObjectId)) { try { - ObjectId oid = converter.convertObjectId(id); + ObjectId oid = conversionService.convert(id, ObjectId.class); ids.add(oid); } catch (ConversionException ignored) { ids.add(id); @@ -93,9 +93,9 @@ public class QueryMapper { } else { value = getMappedObject((DBObject) value, entity); } - } else if (null != converter) { + } else { try { - value = converter.convertObjectId(value); + value = conversionService.convert(value, ObjectId.class); } catch (ConversionException ignored) { } } diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/AbstractMongoConverter.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/AbstractMongoConverter.java index 51fdb5e57..b1a9240ae 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/AbstractMongoConverter.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/AbstractMongoConverter.java @@ -17,12 +17,6 @@ package org.springframework.data.mongodb.core.convert; import java.math.BigInteger; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.Iterator; -import java.util.List; -import java.util.Map; - import org.bson.types.ObjectId; import org.springframework.beans.factory.InitializingBean; import org.springframework.core.convert.ConversionService; @@ -35,10 +29,6 @@ import org.springframework.data.mongodb.core.convert.MongoConverters.ObjectIdToS import org.springframework.data.mongodb.core.convert.MongoConverters.StringToBigIntegerConverter; import org.springframework.data.mongodb.core.convert.MongoConverters.StringToObjectIdConverter; -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}. @@ -114,77 +104,4 @@ public abstract class AbstractMongoConverter implements MongoConverter, Initiali public void afterPropertiesSet() { initializeConverters(); } - - @SuppressWarnings("unchecked") - public Object maybeConvertObject(Object obj) { - - if (obj == null) { - return null; - } - - if (obj instanceof Enum) { - return ((Enum) obj).name(); - } - - if (null != obj && conversions.isSimpleType(obj.getClass())) { - // Doesn't need conversion - return obj; - } - - if (obj instanceof BasicDBList) { - return maybeConvertList((BasicDBList) obj); - } - - if (obj instanceof DBObject) { - DBObject newValueDbo = new BasicDBObject(); - for (String vk : ((DBObject) obj).keySet()) { - Object o = ((DBObject) obj).get(vk); - newValueDbo.put(vk, maybeConvertObject(o)); - } - return newValueDbo; - } - - if (obj instanceof Map) { - Map m = new HashMap(); - for (Map.Entry entry : ((Map) obj).entrySet()) { - m.put(entry.getKey(), maybeConvertObject(entry.getValue())); - } - return m; - } - - if (obj instanceof List) { - List l = (List) obj; - List newList = new ArrayList(); - for (Object o : l) { - newList.add(maybeConvertObject(o)); - } - return newList; - } - - if (obj.getClass().isArray()) { - return maybeConvertArray((Object[]) obj); - } - - DBObject newDbo = new BasicDBObject(); - this.write(obj, newDbo); - return newDbo; - } - - public Object[] maybeConvertArray(Object[] src) { - Object[] newArr = new Object[src.length]; - for (int i = 0; i < src.length; i++) { - newArr[i] = maybeConvertObject(src[i]); - } - return newArr; - } - - public BasicDBList maybeConvertList(BasicDBList dbl) { - BasicDBList newDbl = new BasicDBList(); - Iterator iter = dbl.iterator(); - while (iter.hasNext()) { - Object o = iter.next(); - newDbl.add(maybeConvertObject(o)); - } - return newDbl; - } } diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/CustomConversions.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/CustomConversions.java index ab8fe8f3b..319b9d626 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/CustomConversions.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/CustomConversions.java @@ -41,56 +41,57 @@ 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}. + * 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>(); - + this.converters = new ArrayList(); this.converters.add(CustomToStringConverter.INSTANCE); this.converters.add(BigDecimalToStringConverter.INSTANCE); this.converters.add(StringToBigDecimalConverter.INSTANCE); this.converters.addAll(converters); - + for (Object c : this.converters) { registerConversion(c); } - + this.simpleTypeHolder = new SimpleTypeHolder(customSimpleTypes, true); } - + /** * Returns the underlying {@link SimpleTypeHolder}. * @@ -99,7 +100,7 @@ public class CustomConversions { public SimpleTypeHolder getSimpleTypeHolder() { return simpleTypeHolder; } - + /** * Returns whether the given type is considered to be simple. * @@ -109,33 +110,33 @@ public class CustomConversions { 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!"); } @@ -149,13 +150,13 @@ public class CustomConversions { * @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){ + } else if (converter instanceof Converter) { Class[] arguments = GenericTypeResolver.resolveTypeArguments(converter.getClass(), Converter.class); register(new ConvertiblePair(arguments[0], arguments[1])); } else { @@ -170,22 +171,33 @@ public class CustomConversions { * @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 to convert to in case we have a custom conversion registered to convert the given source + * type into a Mongo native one. + * + * @param source must not be {@literal null} + * @return + */ + public Class getCustomWriteTarget(Class source) { + return getCustomWriteTarget(source, null); + } + /** * 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. + * first target type matching or {@literal null} if no conversion can be found. * * @param source must not be {@literal null} * @param expectedTargetType @@ -195,7 +207,30 @@ public class CustomConversions { Assert.notNull(source); return getCustomTarget(source, expectedTargetType, writingPairs); } - + + /** + * Returns whether we have a custom conversion registered to write into a Mongo native type. The returned type might + * be a subclass oth the given expected type though. + * + * @param source must not be {@literal null} + * @return + */ + public boolean hasCustomWriteTarget(Class source) { + return hasCustomWriteTarget(source, null); + } + + /** + * Returns whether we have a custom conversion registered to write an object of the given source type into an object + * of the given Mongo native target type. + * + * @param source must not be {@literal null}. + * @param expectedTargetType + * @return + */ + public boolean hasCustomWriteTarget(Class source, Class expectedTargetType) { + return getCustomWriteTarget(source, expectedTargetType) != null; + } + /** * Returns whether we have a custom conversion registered to read the given source into the given target type. * @@ -219,10 +254,10 @@ public class CustomConversions { * @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(); @@ -234,7 +269,7 @@ public class CustomConversions { return null; } - + /** * Returns whether the given type is a type that Mongo can handle basically. * @@ -244,9 +279,8 @@ public class CustomConversions { private static boolean isMongoBasicType(Class type) { return MONGO_TYPES.contains(type); } - - - private enum CustomToStringConverter implements GenericConverter { + + private enum CustomToStringConverter implements GenericConverter { INSTANCE; public Set getConvertibleTypes() { 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 a5e749a38..60675a43e 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 @@ -23,6 +23,8 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Collections; +import java.util.HashMap; +import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Map.Entry; @@ -129,22 +131,6 @@ public class MappingMongoConverter extends AbstractMongoConverter implements App this.applicationContext = applicationContext; } - /* - * (non-Javadoc) - * @see org.springframework.data.mongodb.core.core.convert.MongoConverter#convertObjectId(org.bson.types.ObjectId, java.lang.Class) - */ - public T convertObjectId(ObjectId id, Class targetType) { - return conversionService.convert(id, targetType); - } - - /* - * (non-Javadoc) - * @see org.springframework.data.mongodb.core.core.convert.MongoConverter#convertObjectId(java.lang.Object) - */ - public ObjectId convertObjectId(Object id) { - return conversionService.convert(id, ObjectId.class); - } - /* * (non-Javadoc) * @see org.springframework.data.mongodb.core.core.MongoReader#read(java.lang.Class, com.mongodb.DBObject) @@ -859,4 +845,78 @@ public class MappingMongoConverter extends AbstractMongoConverter implements App } return rootList; } + + @SuppressWarnings("unchecked") + public Object convertToMongoType(Object obj) { + + if (obj == null) { + return null; + } + + Class target = conversions.getCustomWriteTarget(getClass()); + if (target != null) { + return conversionService.convert(obj, target); + } + + if (null != obj && conversions.isSimpleType(obj.getClass())) { + // Doesn't need conversion + return getPotentiallyConvertedSimpleWrite(obj); + } + + if (obj instanceof BasicDBList) { + return maybeConvertList((BasicDBList) obj); + } + + if (obj instanceof DBObject) { + DBObject newValueDbo = new BasicDBObject(); + for (String vk : ((DBObject) obj).keySet()) { + Object o = ((DBObject) obj).get(vk); + newValueDbo.put(vk, convertToMongoType(o)); + } + return newValueDbo; + } + + if (obj instanceof Map) { + Map m = new HashMap(); + for (Map.Entry entry : ((Map) obj).entrySet()) { + m.put(entry.getKey(), convertToMongoType(entry.getValue())); + } + return m; + } + + if (obj instanceof List) { + List l = (List) obj; + List newList = new ArrayList(); + for (Object o : l) { + newList.add(convertToMongoType(o)); + } + return newList; + } + + if (obj.getClass().isArray()) { + return maybeConvertArray((Object[]) obj); + } + + DBObject newDbo = new BasicDBObject(); + this.write(obj, newDbo); + return newDbo; + } + + public Object[] maybeConvertArray(Object[] src) { + Object[] newArr = new Object[src.length]; + for (int i = 0; i < src.length; i++) { + newArr[i] = convertToMongoType(src[i]); + } + return newArr; + } + + public BasicDBList maybeConvertList(BasicDBList dbl) { + BasicDBList newDbl = new BasicDBList(); + Iterator iter = dbl.iterator(); + while (iter.hasNext()) { + Object o = iter.next(); + newDbl.add(convertToMongoType(o)); + } + return newDbl; + } } diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/MongoConverter.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/MongoConverter.java index 58ea75e25..d80dcaf50 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/MongoConverter.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/MongoConverter.java @@ -15,43 +15,29 @@ */ package org.springframework.data.mongodb.core.convert; -import com.mongodb.BasicDBList; -import org.bson.types.ObjectId; import org.springframework.core.convert.ConversionService; import org.springframework.data.mapping.context.MappingContext; import org.springframework.data.mongodb.core.mapping.MongoPersistentEntity; import org.springframework.data.mongodb.core.mapping.MongoPersistentProperty; +/** + * Central Mongo specific converter interface which combines {@link MongoWriter} and {@link MongoReader}. + * + * @author Oliver Gierke + */ public interface MongoConverter extends MongoWriter, MongoReader { /** - * Converts the given {@link ObjectId} to the given target type. + * Returns the underlying {@link MappingContext} used by the converter. * - * @param - * the actual type to create - * @param id - * the source {@link ObjectId} - * @param targetType - * the target type to convert the {@link ObjectId} to - * @return + * @return never {@literal null} */ - public T convertObjectId(ObjectId id, Class targetType); - - /** - * Returns the {@link ObjectId} instance for the given id. - * - * @param id - * @return - */ - public ObjectId convertObjectId(Object id); - MappingContext, MongoPersistentProperty> getMappingContext(); - - Object maybeConvertObject(Object obj); - - Object[] maybeConvertArray(Object[] src); - - BasicDBList maybeConvertList(BasicDBList dbl); + /** + * Returns the underlying {@link ConversionService} used by the converter. + * + * @return never {@literal null}. + */ ConversionService getConversionService(); } diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/MongoWriter.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/MongoWriter.java index 65f09c653..72a68c84c 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/MongoWriter.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/MongoWriter.java @@ -20,10 +20,10 @@ import com.mongodb.DBObject; /** * A MongoWriter is responsible for converting an object of type T to the native MongoDB representation DBObject. * - * @param - * the type of the object to convert to a DBObject + * @param the type of the object to convert to a DBObject * @author Mark Pollack * @author Thomas Risberg + * @author Oliver Gierke */ public interface MongoWriter { @@ -36,5 +36,13 @@ public interface MongoWriter { * The DBObject to use for writing. */ void write(T t, DBObject dbo); - + + /** + * Converts the given object into one Mongo will be able to store natively. If the given object can already be stored + * as is, no conversion will happen. + * + * @param obj + * @return + */ + Object convertToMongoType(Object obj); } diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/SimpleMongoConverter.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/SimpleMongoConverter.java deleted file mode 100644 index cc65752a7..000000000 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/SimpleMongoConverter.java +++ /dev/null @@ -1,516 +0,0 @@ -/* - * Copyright 2010-2011 the original author or authors. - * - * 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.mongodb.core.convert; - -import java.lang.reflect.Array; -import java.lang.reflect.GenericArrayType; -import java.lang.reflect.ParameterizedType; -import java.lang.reflect.Type; -import java.lang.reflect.TypeVariable; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collection; -import java.util.Collections; -import java.util.Date; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Locale; -import java.util.Map; -import java.util.Map.Entry; -import java.util.Set; -import java.util.regex.Pattern; - -import com.mongodb.BasicDBList; -import com.mongodb.BasicDBObject; -import com.mongodb.DBObject; -import com.mongodb.DBRef; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; -import org.bson.types.CodeWScope; -import org.bson.types.ObjectId; -import org.springframework.beans.BeanUtils; -import org.springframework.beans.factory.InitializingBean; -import org.springframework.core.CollectionFactory; -import org.springframework.core.convert.ConversionFailedException; -import org.springframework.core.convert.ConversionService; -import org.springframework.core.convert.converter.Converter; -import org.springframework.core.convert.support.ConversionServiceFactory; -import org.springframework.data.mapping.context.MappingContext; -import org.springframework.data.mongodb.core.convert.MongoPropertyDescriptors.MongoPropertyDescriptor; -import org.springframework.data.mongodb.core.mapping.MongoPersistentEntity; -import org.springframework.data.mongodb.core.mapping.MongoPersistentProperty; -import org.springframework.data.mongodb.core.mapping.SimpleMongoMappingContext; -import org.springframework.util.Assert; -import org.springframework.util.comparator.CompoundComparator; - -/** - * Basic {@link MongoConverter} implementation to convert between domain classes and {@link DBObject}s. - * - * @author Mark Pollack - * @author Thomas Risberg - * @author Oliver Gierke - * - * @deprecated since Spring 1.0 M3 in favor of {@link org.springframework.data.mongodb.core.core.convert.MappingMongoConverter} - * The MappingMongoConverter provides all the functionality of the SimpleMongoConverter and will replace it as the default - * converter used. The SimpleMongoCOnverter will be removed at some point before the GA release. - */ -@Deprecated -public class SimpleMongoConverter extends AbstractMongoConverter implements InitializingBean { - - private static final Log LOG = LogFactory.getLog(SimpleMongoConverter.class); - @SuppressWarnings("unchecked") - private static final List> MONGO_TYPES = Arrays.asList(Number.class, Date.class, String.class, - DBObject.class); - private static final Set SIMPLE_TYPES; - - static { - Set basics = new HashSet(); - basics.add(boolean.class.getName()); - basics.add(long.class.getName()); - basics.add(short.class.getName()); - basics.add(int.class.getName()); - basics.add(byte.class.getName()); - basics.add(float.class.getName()); - basics.add(double.class.getName()); - basics.add(char.class.getName()); - basics.add(Boolean.class.getName()); - basics.add(Long.class.getName()); - basics.add(Short.class.getName()); - basics.add(Integer.class.getName()); - basics.add(Byte.class.getName()); - basics.add(Float.class.getName()); - basics.add(Double.class.getName()); - basics.add(Character.class.getName()); - basics.add(String.class.getName()); - basics.add(java.util.Date.class.getName()); - // basics.add(Time.class.getName()); - // basics.add(Timestamp.class.getName()); - // basics.add(java.sql.Date.class.getName()); - // basics.add(BigDecimal.class.getName()); - // basics.add(BigInteger.class.getName()); - basics.add(Locale.class.getName()); - // basics.add(Calendar.class.getName()); - // basics.add(GregorianCalendar.class.getName()); - // basics.add(java.util.Currency.class.getName()); - // basics.add(TimeZone.class.getName()); - // basics.add(Object.class.getName()); - basics.add(Class.class.getName()); - // basics.add(byte[].class.getName()); - // basics.add(Byte[].class.getName()); - // basics.add(char[].class.getName()); - // basics.add(Character[].class.getName()); - // basics.add(Blob.class.getName()); - // basics.add(Clob.class.getName()); - // basics.add(Serializable.class.getName()); - // basics.add(URI.class.getName()); - // basics.add(URL.class.getName()); - basics.add(DBRef.class.getName()); - basics.add(Pattern.class.getName()); - basics.add(CodeWScope.class.getName()); - basics.add(ObjectId.class.getName()); - basics.add(Enum.class.getName()); - SIMPLE_TYPES = Collections.unmodifiableSet(basics); - } - - private final MappingContext, MongoPersistentProperty> mappingContext; - - /** - * Creates a {@link SimpleMongoConverter}. - */ - public SimpleMongoConverter() { - super(ConversionServiceFactory.createDefaultConversionService()); - this.mappingContext = new SimpleMongoMappingContext(); - } - - /* (non-Javadoc) - * @see org.springframework.data.mongodb.core.core.convert.MongoConverter#getMappingContext() - */ - public MappingContext, MongoPersistentProperty> getMappingContext() { - return mappingContext; - } - - /* - * (non-Javadoc) - * - * @see org.springframework.data.mongodb.core.core.MongoWriter#write(java.lang.Object, com.mongodb.DBObject) - */ - @SuppressWarnings("rawtypes") - public void write(Object obj, DBObject dbo) { - - MongoBeanWrapper beanWrapper = createWrapper(obj, false); - for (MongoPropertyDescriptor descriptor : beanWrapper.getDescriptors()) { - if (descriptor.isMappable()) { - Object value = beanWrapper.getValue(descriptor); - - if (value == null) { - continue; - } - - String keyToUse = descriptor.getKeyToMap(); - if (descriptor.isEnum()) { - writeValue(dbo, keyToUse, ((Enum) value).name()); - } else if (descriptor.isIdProperty() && descriptor.isOfIdType()) { - if (value instanceof String && ObjectId.isValid((String) value)) { - try { - writeValue(dbo, keyToUse, conversionService.convert(value, ObjectId.class)); - } catch (ConversionFailedException iae) { - LOG.warn("Unable to convert the String " + value + " to an ObjectId"); - writeValue(dbo, keyToUse, value); - } - } else { - // we can't convert this id - use as is - writeValue(dbo, keyToUse, value); - } - } else { - writeValue(dbo, keyToUse, value); - } - } else { - if (!"class".equals(descriptor.getName())) { - LOG.debug("Skipping property " + descriptor.getName() + " as it's not a mappable one."); - } - } - } - } - - /** - * Writes the given value to the given {@link DBObject}. Will skip {@literal null} values. - * - * @param dbo - * @param keyToUse - * @param value - */ - private void writeValue(DBObject dbo, String keyToUse, Object value) { - - if (!isSimpleType(value.getClass())) { - writeCompoundValue(dbo, keyToUse, value); - } else { - dbo.put(keyToUse, value); - } - } - - /** - * Writes the given {@link CompoundComparator} value to the given {@link DBObject}. - * - * @param dbo - * @param keyToUse - * @param value - */ - @SuppressWarnings("unchecked") - private void writeCompoundValue(DBObject dbo, String keyToUse, Object value) { - if (value instanceof Map) { - writeMap(dbo, keyToUse, (Map) value); - return; - } - if (value instanceof Collection) { - // Should write a collection! - writeArray(dbo, keyToUse, ((Collection) value).toArray()); - return; - } - if (value instanceof Object[]) { - // Should write an array! - writeArray(dbo, keyToUse, (Object[]) value); - return; - } - - Class customTargetType = getCustomTargetType(value); - if (customTargetType != null) { - dbo.put(keyToUse, conversionService.convert(value, customTargetType)); - return; - } - - DBObject nestedDbo = new BasicDBObject(); - write(value, nestedDbo); - dbo.put(keyToUse, nestedDbo); - } - - /** - * Returns whether the {@link ConversionService} has a custom {@link Converter} registered that can convert the given - * object into one of the types supported by MongoDB. - * - * @param obj - * @return - */ - private Class getCustomTargetType(Object obj) { - - for (Class mongoType : MONGO_TYPES) { - if (conversionService.canConvert(obj.getClass(), mongoType)) { - return mongoType; - } - } - return null; - } - - /** - * Writes the given {@link Map} to the given {@link DBObject}. - * - * @param dbo - * @param mapKey - * @param map - */ - protected void writeMap(DBObject dbo, String mapKey, Map map) { - // TODO support non-string based keys as long as there is a Spring Converter obj->string and (optionally) - // string->obj - DBObject dboToPopulate = null; - - // TODO - Does that make sense? If we create a new object here it's content will never make it out of this - // method - if (mapKey != null) { - dboToPopulate = new BasicDBObject(); - } else { - dboToPopulate = dbo; - } - if (map != null) { - for (Entry entry : map.entrySet()) { - - Object entryValue = entry.getValue(); - String entryKey = entry.getKey(); - - if (!isSimpleType(entryValue.getClass())) { - writeCompoundValue(dboToPopulate, entryKey, entryValue); - } else { - dboToPopulate.put(entryKey, entryValue); - } - } - dbo.put(mapKey, dboToPopulate); - } - } - - /** - * Writes the given array to the given {@link DBObject}. - * - * @param dbo - * @param keyToUse - * @param array - */ - protected void writeArray(DBObject dbo, String keyToUse, Object[] array) { - Object[] dboValues; - if (array != null) { - dboValues = new Object[array.length]; - int i = 0; - for (Object o : array) { - if (!isSimpleType(o.getClass())) { - DBObject dboValue = new BasicDBObject(); - write(o, dboValue); - dboValues[i] = dboValue; - } else { - dboValues[i] = o; - } - i++; - } - dbo.put(keyToUse, dboValues); - } - } - - /* - * (non-Javadoc) - * - * @see org.springframework.data.mongodb.core.core.MongoReader#read(java.lang.Class, com.mongodb.DBObject) - */ - public S read(Class clazz, DBObject source) { - - if (source == null) { - return null; - } - - Assert.notNull(clazz, "Mapped class was not specified"); - S target = BeanUtils.instantiateClass(clazz); - MongoBeanWrapper bw = new MongoBeanWrapper(target, conversionService, true); - - for (MongoPropertyDescriptor descriptor : bw.getDescriptors()) { - String keyToUse = descriptor.getKeyToMap(); - if (source.containsField(keyToUse)) { - if (descriptor.isMappable()) { - Object value = source.get(keyToUse); - if (!isSimpleType(value.getClass())) { - if (value instanceof Object[]) { - bw.setValue(descriptor, readCollection(descriptor, Arrays.asList((Object[]) value)).toArray()); - } else if (value instanceof BasicDBList) { - bw.setValue(descriptor, readCollection(descriptor, (BasicDBList) value)); - } else if (value instanceof DBObject) { - bw.setValue(descriptor, readCompoundValue(descriptor, (DBObject) value)); - } else { - LOG.warn("Unable to map compound DBObject field " + keyToUse + " to property " + descriptor.getName() - + ". The field value should have been a 'DBObject.class' but was " + value.getClass().getName()); - } - } else { - bw.setValue(descriptor, value); - } - } else { - LOG.warn("Unable to map DBObject field " + keyToUse + " to property " + descriptor.getName() + ". Skipping."); - } - } - } - - return target; - } - - /** - * Reads the given collection values (that are {@link DBObject}s potentially) into a {@link Collection} of domain - * objects. - * - * @param descriptor - * @param values - * @return - */ - private Collection readCollection(MongoPropertyDescriptor descriptor, Collection values) { - - Class targetCollectionType = descriptor.getPropertyType(); - boolean targetIsArray = targetCollectionType.isArray(); - - @SuppressWarnings("unchecked") - Collection result = targetIsArray ? new ArrayList(values.size()) : CollectionFactory - .createCollection(targetCollectionType, values.size()); - - for (Object o : values) { - if (o instanceof DBObject) { - Class type; - if (targetIsArray) { - type = targetCollectionType.getComponentType(); - } else { - type = getGenericParameters(descriptor.getTypeToSet()).get(0); - } - result.add(read(type, (DBObject) o)); - } else { - result.add(o); - } - } - - return result; - } - - /** - * Reads a compound value from the given {@link DBObject} for the given property. - * - * @param pd - * @param dbo - * @return - */ - private Object readCompoundValue(MongoPropertyDescriptor pd, DBObject dbo) { - - Assert.isTrue(!pd.isCollection(), "Collections not supported!"); - - if (pd.isMap()) { - return readMap(pd, dbo, getGenericParameters(pd.getTypeToSet()).get(1)); - } else { - return read(pd.getPropertyType(), dbo); - } - } - - /** - * Create a {@link Map} instance. Will return a {@link HashMap} by default. Subclasses might want to override this - * method to use a custom {@link Map} implementation. - * - * @return - */ - protected Map createMap() { - return new HashMap(); - } - - /** - * Reads every key/value pair from the {@link DBObject} into a {@link Map} instance. - * - * @param pd - * @param dbo - * @param targetType - * @return - */ - protected Map readMap(MongoPropertyDescriptor pd, DBObject dbo, Class targetType) { - Map map = createMap(); - for (String key : dbo.keySet()) { - Object value = dbo.get(key); - if (!isSimpleType(value.getClass())) { - map.put(key, read(targetType, (DBObject) value)); - // Can do some reflection tricks here - - // throw new RuntimeException("User types not supported yet as values for Maps"); - } else { - map.put(key, conversionService.convert(value, targetType)); - } - } - return map; - } - - protected static boolean isSimpleType(Class propertyType) { - if (propertyType == null) { - return false; - } - if (propertyType.isArray()) { - return isSimpleType(propertyType.getComponentType()); - } - return SIMPLE_TYPES.contains(propertyType.getName()); - } - - /** - * Callback to allow customizing creation of a {@link MongoBeanWrapper}. - * - * @param target - * the target object to wrap - * @param fieldAccess - * whether to use field access or property access - * @return - */ - protected MongoBeanWrapper createWrapper(Object target, boolean fieldAccess) { - - return new MongoBeanWrapper(target, conversionService, fieldAccess); - } - - public List> getGenericParameters(Type genericParameterType) { - - List> actualGenericParameterTypes = new ArrayList>(); - - if (genericParameterType instanceof ParameterizedType) { - ParameterizedType aType = (ParameterizedType) genericParameterType; - Type[] parameterArgTypes = aType.getActualTypeArguments(); - for (Type parameterArgType : parameterArgTypes) { - if (parameterArgType instanceof GenericArrayType) { - Class arrayType = (Class) ((GenericArrayType) parameterArgType).getGenericComponentType(); - actualGenericParameterTypes.add(Array.newInstance(arrayType, 0).getClass()); - } else { - if (parameterArgType instanceof ParameterizedType) { - ParameterizedType paramTypeArgs = (ParameterizedType) parameterArgType; - actualGenericParameterTypes.add((Class) paramTypeArgs.getRawType()); - } else { - if (parameterArgType instanceof TypeVariable) { - throw new RuntimeException("Can not map " + ((TypeVariable) parameterArgType).getName()); - } else { - if (parameterArgType instanceof Class) { - actualGenericParameterTypes.add((Class) parameterArgType); - } else { - throw new RuntimeException("Can not map " + parameterArgType); - } - } - } - } - } - } - - return actualGenericParameterTypes; - } - - /* (non-Javadoc) - * @see org.springframework.data.mongodb.core.core.convert.MongoConverter#convertObjectId(org.bson.types.ObjectId, java.lang.Class) - */ - public T convertObjectId(ObjectId id, Class targetType) { - return conversionService.convert(id, targetType); - } - - /* (non-Javadoc) - * @see org.springframework.data.mongodb.core.core.convert.MongoConverter#convertObjectId(java.lang.Object) - */ - public ObjectId convertObjectId(Object id) { - return conversionService.convert(id, ObjectId.class); - } -} diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/ConvertingParameterAccessor.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/ConvertingParameterAccessor.java index 44d23cde8..2f9a8d751 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/ConvertingParameterAccessor.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/ConvertingParameterAccessor.java @@ -15,19 +15,17 @@ */ package org.springframework.data.mongodb.repository; -import java.util.HashMap; import java.util.Iterator; -import java.util.Map; -import com.mongodb.BasicDBList; -import com.mongodb.BasicDBObject; -import com.mongodb.DBObject; import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Sort; import org.springframework.data.mongodb.core.convert.MappingMongoConverter; import org.springframework.data.mongodb.core.convert.MongoWriter; import org.springframework.data.repository.query.ParameterAccessor; +import com.mongodb.BasicDBList; +import com.mongodb.DBObject; + /** * Custom {@link ParameterAccessor} that uses a {@link MongoWriter} to serialize parameters into Mongo format. * @@ -90,11 +88,8 @@ public class ConvertingParameterAccessor implements ParameterAccessor { * @return */ private Object getConvertedValue(Object value) { - - DBObject result = new BasicDBObject(); - writer.write(new ValueHolder(value), result); - Object resultValue = ((DBObject) result.get("value")).get("value"); - return removeTypeInfoRecursively(resultValue); + + return removeTypeInfoRecursively(writer.convertToMongoType(value)); } /** @@ -181,27 +176,6 @@ public class ConvertingParameterAccessor implements ParameterAccessor { } } - /** - * Simple value holder class to allow conversion and accessing the converted value in a deterministic way. - * - * @author Oliver Gierke - */ - private static class ValueHolder { - - private Map value = new HashMap(); - - public ValueHolder(Object value) { - - this.value.put("value", value); - } - - @SuppressWarnings("unused") - public Map getValue() { - - return value; - } - } - /** * Custom {@link Iterator} that adds a method to access elements in a converted manner. * diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/MongoOperationsUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/MongoOperationsUnitTests.java index 16040a22e..e78587743 100644 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/MongoOperationsUnitTests.java +++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/MongoOperationsUnitTests.java @@ -74,10 +74,6 @@ public abstract class MongoOperationsUnitTests { return (S) person; } - public T convertObjectId(ObjectId id, Class targetType) { - return null; - } - public ObjectId convertObjectId(Object id) { return null; } @@ -85,6 +81,11 @@ public abstract class MongoOperationsUnitTests { public MappingContext, MongoPersistentProperty> getMappingContext() { return null; } + + + public Object convertToMongoType(Object obj) { + return null; + } }; } diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/MongoTemplateTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/MongoTemplateTests.java index 52f9a70b3..a72c88370 100644 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/MongoTemplateTests.java +++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/MongoTemplateTests.java @@ -24,11 +24,6 @@ import java.util.Arrays; import java.util.HashSet; import java.util.List; -import com.mongodb.DBCollection; -import com.mongodb.DBObject; -import com.mongodb.Mongo; -import com.mongodb.MongoException; -import com.mongodb.WriteResult; import org.bson.types.ObjectId; import org.junit.Assert; import org.junit.Before; @@ -41,12 +36,7 @@ import org.springframework.dao.DataAccessException; import org.springframework.dao.DataIntegrityViolationException; import org.springframework.data.mongodb.InvalidMongoDbApiUsageException; import org.springframework.data.mongodb.MongoDbFactory; -import org.springframework.data.mongodb.core.CollectionCallback; -import org.springframework.data.mongodb.core.MongoTemplate; -import org.springframework.data.mongodb.core.WriteResultChecking; import org.springframework.data.mongodb.core.convert.MappingMongoConverter; -import org.springframework.data.mongodb.core.convert.MongoConverter; -import org.springframework.data.mongodb.core.convert.SimpleMongoConverter; import org.springframework.data.mongodb.core.index.Index; import org.springframework.data.mongodb.core.index.Index.Duplicates; import org.springframework.data.mongodb.core.mapping.MongoMappingContext; @@ -57,13 +47,18 @@ import org.springframework.data.mongodb.core.query.Update; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; +import com.mongodb.DBCollection; +import com.mongodb.DBObject; +import com.mongodb.Mongo; +import com.mongodb.MongoException; +import com.mongodb.WriteResult; + /** * Integration test for {@link MongoTemplate}. * * @author Oliver Gierke * @author Thomas Risberg */ -@SuppressWarnings("deprecation") @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration("classpath:infrastructure.xml") public class MongoTemplateTests { @@ -72,7 +67,7 @@ public class MongoTemplateTests { MongoTemplate template; @Autowired MongoDbFactory factory; - MongoTemplate mappingTemplate, simpleTemplate; + MongoTemplate mappingTemplate; @Rule public ExpectedException thrown = ExpectedException.none(); @@ -92,10 +87,6 @@ public class MongoTemplateTests { MappingMongoConverter mappingConverter = new MappingMongoConverter(factory, mappingContext); mappingConverter.afterPropertiesSet(); this.mappingTemplate = new MongoTemplate(factory, mappingConverter); - - SimpleMongoConverter simpleConverter = new SimpleMongoConverter(); - simpleConverter.afterPropertiesSet(); - this.simpleTemplate = new MongoTemplate(factory, simpleConverter); } @Before @@ -118,9 +109,7 @@ public class MongoTemplateTests { person.setAge(25); template.insert(person); - MongoConverter converter = template.getConverter(); - - List result = template.find(new Query(Criteria.where("_id").is(converter.convertObjectId(person.getId()))), + List result = template.find(new Query(Criteria.where("_id").is(person.getId())), Person.class); assertThat(result.size(), is(1)); assertThat(result, hasItem(person)); @@ -175,11 +164,6 @@ public class MongoTemplateTests { assertThat(dropDupes, is(true)); } - @Test - public void testProperHandlingOfDifferentIdTypesWithSimpleMongoConverter() throws Exception { - testProperHandlingOfDifferentIdTypes(this.simpleTemplate); - } - @Test public void testProperHandlingOfDifferentIdTypesWithMappingMongoConverter() throws Exception { testProperHandlingOfDifferentIdTypes(this.mappingTemplate); diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/SimpleMongoConverterTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/SimpleMongoConverterTests.java deleted file mode 100644 index a4b20697d..000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/SimpleMongoConverterTests.java +++ /dev/null @@ -1,439 +0,0 @@ -/* - * Copyright 2010-2011 the original author or authors. - * - * 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.mongodb.core; - -import static org.hamcrest.Matchers.is; -import static org.hamcrest.Matchers.notNullValue; -import static org.hamcrest.Matchers.nullValue; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertThat; -import static org.junit.Assert.assertTrue; - -import java.lang.reflect.Field; -import java.math.BigInteger; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Date; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import org.hamcrest.CoreMatchers; -import org.joda.time.LocalDate; -import org.junit.Before; -import org.junit.Test; -import org.springframework.core.convert.converter.Converter; -import org.springframework.data.mongodb.core.SomeEnumTest.NumberEnum; -import org.springframework.data.mongodb.core.SomeEnumTest.StringEnum; -import org.springframework.data.mongodb.core.convert.CustomConversions; -import org.springframework.data.mongodb.core.convert.SimpleMongoConverter; -import org.springframework.util.ReflectionUtils; - -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 }"; - static final String COMPLEX_JSON = "{ \"map\" : { \"trade\" : { \"orderType\" : \"BUY\" , \"price\" : 90.5 , \"quantity\" : 0 , \"ticker\" : \"VMW\"}}}"; - - SimpleMongoConverter converter; - DBObject object; - - @Before - public void setUp() { - converter = new SimpleMongoConverter(); - converter.afterPropertiesSet(); - object = new BasicDBObject(); - } - - @Test - public void notNestedObject() { - User user = new User(); - user.setAccountName("My Account"); - user.setUserName("Mark"); - converter.write(user, object); - assertEquals("My Account", object.get("accountName")); - assertEquals("Mark", object.get("userName")); - - User u = converter.read(User.class, object); - - assertEquals("My Account", u.getAccountName()); - assertEquals("Mark", u.getUserName()); - } - - @Test - public void nestedObject() { - Portfolio p = createPortfolioWithNoTrades(); - converter.write(p, object); - - assertEquals("High Risk Trading Account", object.get("portfolioName")); - assertTrue(object.containsField("user")); - - Portfolio cp = converter.read(Portfolio.class, object); - - assertEquals("High Risk Trading Account", cp.getPortfolioName()); - assertEquals("Joe Trader", cp.getUser().getUserName()); - assertEquals("ACCT-123", cp.getUser().getAccountName()); - - } - - @Test - public void objectWithMap() { - Portfolio p = createPortfolioWithPositions(); - converter.write(p, object); - - Portfolio cp = converter.read(Portfolio.class, object); - assertEquals("High Risk Trading Account", cp.getPortfolioName()); - } - - @Test - public void objectWithMapContainingNonPrimitiveTypeAsValue() { - Portfolio p = createPortfolioWithManagers(); - converter.write(p, object); - - Portfolio cp = converter.read(Portfolio.class, object); - assertEquals("High Risk Trading Account", cp.getPortfolioName()); - } - - protected Portfolio createPortfolioWithPositions() { - - Portfolio portfolio = new Portfolio(); - portfolio.setPortfolioName("High Risk Trading Account"); - Map positions = new HashMap(); - positions.put("CSCO", 1); - portfolio.setPositions(positions); - return portfolio; - } - - protected Portfolio createPortfolioWithManagers() { - - Portfolio portfolio = new Portfolio(); - portfolio.setPortfolioName("High Risk Trading Account"); - Map managers = new HashMap(); - Person p1 = new Person(); - p1.setFirstName("Mark"); - managers.put("CSCO", p1); - portfolio.setPortfolioManagers(managers); - return portfolio; - } - - protected Portfolio createPortfolioWithNoTrades() { - Portfolio portfolio = new Portfolio(); - User user = new User(); - user.setUserName("Joe Trader"); - user.setAccountName("ACCT-123"); - portfolio.setUser(user); - portfolio.setPortfolioName("High Risk Trading Account"); - return portfolio; - } - - @Test - public void objectWithArrayContainingNonPrimitiveType() { - TradeBatch b = createTradeBatch(); - converter.write(b, object); - - TradeBatch b2 = converter.read(TradeBatch.class, object); - assertEquals(b.getBatchId(), b2.getBatchId()); - assertNotNull(b2.getTradeList()); - assertEquals(b.getTradeList().size(), b2.getTradeList().size()); - assertEquals(b.getTradeList().get(1).getTicker(), b2.getTradeList().get(1).getTicker()); - assertEquals(b.getTrades().length, b2.getTrades().length); - assertEquals(b.getTrades()[1].getTicker(), b2.getTrades()[1].getTicker()); - } - - private TradeBatch createTradeBatch() { - TradeBatch tb = new TradeBatch(); - tb.setBatchId("123456"); - Trade t1 = new Trade(); - t1.setOrderType("BUY"); - t1.setTicker("AAPL"); - t1.setQuantity(1000); - t1.setPrice(320.77D); - Trade t2 = new Trade(); - t2.setOrderType("SELL"); - t2.setTicker("MSFT"); - t2.setQuantity(100); - t2.setPrice(27.92D); - tb.setTrades(new Trade[] { t2, t1 }); - tb.setTradeList(Arrays.asList(new Trade[] { t1, t2 })); - return tb; - } - - @Test - public void objectWithEnumTypes() { - SomeEnumTest test = new SomeEnumTest(); - test.setId("123AAA"); - test.setName("Sven"); - test.setStringEnum(StringEnum.ONE); - test.setNumberEnum(NumberEnum.FIVE); - DBObject dbo = new BasicDBObject(); - converter.write(test, dbo); - - SomeEnumTest results = converter.read(SomeEnumTest.class, dbo); - assertNotNull(results); - assertEquals(test.getId(), results.getId()); - assertEquals(test.getName(), results.getName()); - assertEquals(test.getStringEnum(), results.getStringEnum()); - assertEquals(test.getNumberEnum(), results.getNumberEnum()); - } - - @Test - public void serializesClassWithFinalObjectIdCorrectly() throws Exception { - - BasicDBObject object = new BasicDBObject(); - Person person = new Person("Oliver"); - converter.write(person, object); - - assertThat(object.get("class"), is(nullValue())); - assertThat(object.get("_id"), is((Object) person.getId())); - } - - @Test - public void discoversGenericsForType() throws Exception { - - Field field = ReflectionUtils.findField(Sample.class, "map"); - assertListOfStringAndLong(converter.getGenericParameters(field.getGenericType())); - } - - @Test - public void writesSimpleMapCorrectly() throws Exception { - - Map map = new HashMap(); - map.put("foo", 1L); - map.put("bar", 2L); - - Sample sample = new Sample(); - sample.setMap(map); - sample.setNumber(15L); - - converter.write(sample, object); - - assertThat(object.get("number"), is((Object) 15L)); - - Object result = object.get("map"); - assertTrue(result instanceof Map); - - @SuppressWarnings("unchecked") - Map mapResult = (Map) result; - assertThat(mapResult.size(), is(2)); - assertThat(mapResult.get("foo"), is(1L)); - assertThat(mapResult.get("bar"), is(2L)); - } - - @Test - public void writesComplexMapCorrectly() throws Exception { - - Trade trade = new Trade(); - trade.setOrderType("BUY"); - trade.setTicker("VMW"); - trade.setPrice(90.50d); - - Map map = new HashMap(); - map.put("trade", trade); - - converter.write(new Sample2(map), object); - DBObject tradeDbObject = new BasicDBObject(); - converter.write(trade, tradeDbObject); - - Object result = object.get("map"); - assertTrue(result instanceof Map); - - @SuppressWarnings("unchecked") - Map mapResult = (Map) result; - assertThat(mapResult.size(), is(1)); - assertThat(mapResult.get("trade"), is(tradeDbObject)); - } - - @Test - public void readsMapWithSetterCorrectly() throws Exception { - - DBObject input = (DBObject) JSON.parse(SIMPLE_JSON); - Sample result = converter.read(Sample.class, input); - assertThat(result.getNumber(), is(15L)); - - Map map = result.getMap(); - assertThat(map, is(notNullValue())); - assertThat(map.size(), is(2)); - assertThat(map.get("foo"), is(3L)); - assertThat(map.get("bar"), is(4L)); - } - - @Test - public void readsMapWithFieldOnlyCorrectly() throws Exception { - - DBObject input = (DBObject) JSON.parse(COMPLEX_JSON); - Sample2 result = converter.read(Sample2.class, input); - - Map map = result.getMap(); - - Trade trade = new Trade(); - trade.setOrderType("BUY"); - trade.setTicker("VMW"); - trade.setPrice(90.50d); - - assertThat(map.size(), is(1)); - assertThat(map.get("trade").getTicker(), is("VMW")); - assertThat(map.get("trade").getOrderType(), is("BUY")); - assertThat(map.get("trade").getPrice(), is(90.50d)); - } - - @Test - public void supportsBigIntegerAsIdProperty() throws Exception { - - Sample3 sample3 = new Sample3(); - sample3.id = new BigInteger("4d24809660413b687f5d323e", 16); - converter.write(sample3, object); - assertThat(object.get("_id"), is(notNullValue())); - - Sample3 result = converter.read(Sample3.class, - (DBObject) JSON.parse("{\"_id\" : {\"$oid\" : \"4d24809660413b687f5d323e\" }}")); - assertThat(result.getId().toString(16), is("4d24809660413b687f5d323e")); - } - - @Test - public void convertsAddressCorrectly() { - - Address address = new Address(); - address.city = "New York"; - address.street = "Broadway"; - - DBObject dbObject = new BasicDBObject(); - - converter.write(address, dbObject); - - assertThat(dbObject.get("city").toString(), is("New York")); - assertThat(dbObject.get("street").toString(), is("Broadway")); - - Address result = converter.read(Address.class, dbObject); - assertThat(result.city, is("New York")); - assertThat(result.street, is("Broadway")); - } - - @Test - public void convertsJodaTimeTypesCorrectly() { - - List> converters = new ArrayList>(); - converters.add(new LocalDateToDateConverter()); - converters.add(new DateToLocalDateConverter()); - - converter.setCustomConversions(new CustomConversions(converters)); - converter.afterPropertiesSet(); - - AnotherPerson person = new AnotherPerson(); - person.birthDate = new LocalDate(); - - DBObject dbObject = new BasicDBObject(); - converter.write(person, dbObject); - - assertTrue(dbObject.get("birthDate") instanceof Date); - - AnotherPerson result = converter.read(AnotherPerson.class, dbObject); - assertThat(result.getBirthDate(), is(notNullValue())); - } - - private void assertListOfStringAndLong(List> types) { - - assertThat(types.size(), CoreMatchers.is(2)); - assertEquals(String.class, types.get(0)); - assertEquals(Long.class, types.get(1)); - } - - public static class Sample { - - private Map map; - private Long number; - - public void setMap(Map map) { - this.map = map; - } - - public Map getMap() { - return map; - } - - public void setNumber(Long number) { - this.number = number; - } - - public Long getNumber() { - return number; - } - } - - public static class Sample2 { - - private final Map map; - - protected Sample2() { - this.map = null; - } - - public Sample2(Map map) { - this.map = map; - } - - public Map getMap() { - return map; - } - } - - private static class Sample3 { - - private BigInteger id; - - public BigInteger getId() { - return id; - } - } - - public static class Address { - String street; - String city; - - public String getStreet() { - return street; - } - - public String getCity() { - return city; - } - } - - public static class AnotherPerson { - LocalDate birthDate; - - public LocalDate getBirthDate() { - return birthDate; - } - } - - private class LocalDateToDateConverter implements Converter { - - public Date convert(LocalDate source) { - return source.toDateMidnight().toDate(); - } - } - - private class DateToLocalDateConverter implements Converter { - - public LocalDate convert(Date source) { - return new LocalDate(source.getTime()); - } - } -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/convert/MappingMongoConverterUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/convert/MappingMongoConverterUnitTests.java index d993fdf47..821559104 100644 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/convert/MappingMongoConverterUnitTests.java +++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/convert/MappingMongoConverterUnitTests.java @@ -464,7 +464,7 @@ public class MappingMongoConverterUnitTests { */ @Test public void maybeConvertHandlesNullValuesCorrectly() { - assertThat(converter.maybeConvertObject(null), is(nullValue())); + assertThat(converter.convertToMongoType(null), is(nullValue())); } @Test @@ -522,6 +522,12 @@ public class MappingMongoConverterUnitTests { assertThat(result.get("_id"), is(instanceOf(String.class))); } + public void convertsObjectsIfNecessary() { + + ObjectId id = new ObjectId(); + assertThat(converter.convertToMongoType(id), is((Object) id)); + } + class GenericType { T content; } diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/query/QueryMapperUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/query/QueryMapperUnitTests.java index fa8c45858..fd0c34773 100644 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/query/QueryMapperUnitTests.java +++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/query/QueryMapperUnitTests.java @@ -25,8 +25,8 @@ import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.runners.MockitoJUnitRunner; +import org.springframework.core.convert.ConversionService; import org.springframework.data.mongodb.core.QueryMapper; -import org.springframework.data.mongodb.core.convert.MongoConverter; import org.springframework.data.mongodb.core.mapping.MongoPersistentEntity; import org.springframework.data.mongodb.core.mapping.MongoPersistentProperty; @@ -44,7 +44,7 @@ public class QueryMapperUnitTests { QueryMapper mapper; @Mock - MongoConverter converter; + ConversionService converter; @Mock MongoPersistentEntity entity; @Mock @@ -53,7 +53,7 @@ public class QueryMapperUnitTests { @Before public void setUp() { when(entity.getIdProperty()).thenReturn(property); - when(converter.convertObjectId(any())).thenReturn(new ObjectId()); + when(converter.convert(any(), eq(ObjectId.class))).thenReturn(new ObjectId()); mapper = new QueryMapper(converter); }