DATADOC-214 - Cleaned up MongoConverter interface and implementations.
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(…).
This commit is contained in:
@@ -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()) {
|
||||
|
||||
@@ -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<Object> ids = new ArrayList<Object>();
|
||||
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) {
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<Object, Object> m = new HashMap<Object, Object>();
|
||||
for (Map.Entry<Object, Object> entry : ((Map<Object, Object>) obj).entrySet()) {
|
||||
m.put(entry.getKey(), maybeConvertObject(entry.getValue()));
|
||||
}
|
||||
return m;
|
||||
}
|
||||
|
||||
if (obj instanceof List) {
|
||||
List<?> l = (List<?>) obj;
|
||||
List<Object> newList = new ArrayList<Object>();
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<Class<?>> MONGO_TYPES = Arrays.asList(Number.class, Date.class, String.class,
|
||||
DBObject.class);
|
||||
|
||||
|
||||
private final Set<ConvertiblePair> readingPairs;
|
||||
private final Set<ConvertiblePair> writingPairs;
|
||||
private final Set<Class<?>> customSimpleTypes;
|
||||
private final SimpleTypeHolder simpleTypeHolder;
|
||||
|
||||
|
||||
private final List<Object> converters;
|
||||
|
||||
|
||||
/**
|
||||
* Creates an empty {@link CustomConversions} object.
|
||||
*/
|
||||
CustomConversions() {
|
||||
this(new ArrayList<Object>());
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Creates a new {@link CustomConversions} instance registering the given converters.
|
||||
*
|
||||
* @param converters
|
||||
*/
|
||||
public CustomConversions(List<?> converters) {
|
||||
|
||||
|
||||
Assert.notNull(converters);
|
||||
|
||||
|
||||
this.readingPairs = new HashSet<ConvertiblePair>();
|
||||
this.writingPairs = new HashSet<ConvertiblePair>();
|
||||
this.customSimpleTypes = new HashSet<Class<?>>();
|
||||
|
||||
|
||||
this.converters = new ArrayList<Object>();
|
||||
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<ConvertiblePair> 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<ConvertiblePair> getConvertibleTypes() {
|
||||
|
||||
@@ -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> T convertObjectId(ObjectId id, Class<T> 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<Object, Object> m = new HashMap<Object, Object>();
|
||||
for (Map.Entry<Object, Object> entry : ((Map<Object, Object>) obj).entrySet()) {
|
||||
m.put(entry.getKey(), convertToMongoType(entry.getValue()));
|
||||
}
|
||||
return m;
|
||||
}
|
||||
|
||||
if (obj instanceof List) {
|
||||
List<?> l = (List<?>) obj;
|
||||
List<Object> newList = new ArrayList<Object>();
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<Object>, MongoReader<Object> {
|
||||
|
||||
/**
|
||||
* Converts the given {@link ObjectId} to the given target type.
|
||||
* Returns the underlying {@link MappingContext} used by the converter.
|
||||
*
|
||||
* @param <T>
|
||||
* 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> T convertObjectId(ObjectId id, Class<T> targetType);
|
||||
|
||||
/**
|
||||
* Returns the {@link ObjectId} instance for the given id.
|
||||
*
|
||||
* @param id
|
||||
* @return
|
||||
*/
|
||||
public ObjectId convertObjectId(Object id);
|
||||
|
||||
MappingContext<? extends MongoPersistentEntity<?>, 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();
|
||||
}
|
||||
|
||||
@@ -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 <T>
|
||||
* the type of the object to convert to a DBObject
|
||||
* @param <T> the type of the object to convert to a DBObject
|
||||
* @author Mark Pollack
|
||||
* @author Thomas Risberg
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
public interface MongoWriter<T> {
|
||||
|
||||
@@ -36,5 +36,13 @@ public interface MongoWriter<T> {
|
||||
* 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);
|
||||
}
|
||||
|
||||
@@ -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<Class<?>> MONGO_TYPES = Arrays.asList(Number.class, Date.class, String.class,
|
||||
DBObject.class);
|
||||
private static final Set<String> SIMPLE_TYPES;
|
||||
|
||||
static {
|
||||
Set<String> basics = new HashSet<String>();
|
||||
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<? extends MongoPersistentEntity<?>, 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<? extends MongoPersistentEntity<?>, 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<String, Object>) value);
|
||||
return;
|
||||
}
|
||||
if (value instanceof Collection) {
|
||||
// Should write a collection!
|
||||
writeArray(dbo, keyToUse, ((Collection<Object>) 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<String, Object> 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<String, Object> 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> S read(Class<S> 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<Object> readCollection(MongoPropertyDescriptor descriptor, Collection<?> values) {
|
||||
|
||||
Class<?> targetCollectionType = descriptor.getPropertyType();
|
||||
boolean targetIsArray = targetCollectionType.isArray();
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
Collection<Object> result = targetIsArray ? new ArrayList<Object>(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<String, Object> createMap() {
|
||||
return new HashMap<String, Object>();
|
||||
}
|
||||
|
||||
/**
|
||||
* 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<String, Object> 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<Class<?>> getGenericParameters(Type genericParameterType) {
|
||||
|
||||
List<Class<?>> actualGenericParameterTypes = new ArrayList<Class<?>>();
|
||||
|
||||
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> T convertObjectId(ObjectId id, Class<T> 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);
|
||||
}
|
||||
}
|
||||
@@ -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<String, Object> value = new HashMap<String, Object>();
|
||||
|
||||
public ValueHolder(Object value) {
|
||||
|
||||
this.value.put("value", value);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public Map<String, Object> getValue() {
|
||||
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Custom {@link Iterator} that adds a method to access elements in a converted manner.
|
||||
*
|
||||
|
||||
@@ -74,10 +74,6 @@ public abstract class MongoOperationsUnitTests {
|
||||
return (S) person;
|
||||
}
|
||||
|
||||
public <T> T convertObjectId(ObjectId id, Class<T> targetType) {
|
||||
return null;
|
||||
}
|
||||
|
||||
public ObjectId convertObjectId(Object id) {
|
||||
return null;
|
||||
}
|
||||
@@ -85,6 +81,11 @@ public abstract class MongoOperationsUnitTests {
|
||||
public MappingContext<? extends MongoPersistentEntity<?>, MongoPersistentProperty> getMappingContext() {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
public Object convertToMongoType(Object obj) {
|
||||
return null;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -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<Person> result = template.find(new Query(Criteria.where("_id").is(converter.convertObjectId(person.getId()))),
|
||||
List<Person> 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);
|
||||
|
||||
@@ -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<String, Integer> positions = new HashMap<String, Integer>();
|
||||
positions.put("CSCO", 1);
|
||||
portfolio.setPositions(positions);
|
||||
return portfolio;
|
||||
}
|
||||
|
||||
protected Portfolio createPortfolioWithManagers() {
|
||||
|
||||
Portfolio portfolio = new Portfolio();
|
||||
portfolio.setPortfolioName("High Risk Trading Account");
|
||||
Map<String, Person> managers = new HashMap<String, Person>();
|
||||
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<String, Long> map = new HashMap<String, Long>();
|
||||
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<String, Long> mapResult = (Map<String, Long>) 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<String, Trade> map = new HashMap<String, Trade>();
|
||||
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<String, DBObject> mapResult = (Map<String, DBObject>) 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<String, Long> 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<String, Trade> 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<Converter<?, ?>> converters = new ArrayList<Converter<?, ?>>();
|
||||
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<Class<?>> 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<String, Long> map;
|
||||
private Long number;
|
||||
|
||||
public void setMap(Map<String, Long> map) {
|
||||
this.map = map;
|
||||
}
|
||||
|
||||
public Map<String, Long> getMap() {
|
||||
return map;
|
||||
}
|
||||
|
||||
public void setNumber(Long number) {
|
||||
this.number = number;
|
||||
}
|
||||
|
||||
public Long getNumber() {
|
||||
return number;
|
||||
}
|
||||
}
|
||||
|
||||
public static class Sample2 {
|
||||
|
||||
private final Map<String, Trade> map;
|
||||
|
||||
protected Sample2() {
|
||||
this.map = null;
|
||||
}
|
||||
|
||||
public Sample2(Map<String, Trade> map) {
|
||||
this.map = map;
|
||||
}
|
||||
|
||||
public Map<String, Trade> 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<LocalDate, Date> {
|
||||
|
||||
public Date convert(LocalDate source) {
|
||||
return source.toDateMidnight().toDate();
|
||||
}
|
||||
}
|
||||
|
||||
private class DateToLocalDateConverter implements Converter<Date, LocalDate> {
|
||||
|
||||
public LocalDate convert(Date source) {
|
||||
return new LocalDate(source.getTime());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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> {
|
||||
T content;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user