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); }