DATADOC-114 - Fixes for updateFirst/updateMulti not converting POJOs correctly
This commit is contained in:
@@ -136,7 +136,6 @@ public class MongoTemplate implements MongoOperations, ApplicationEventPublisher
|
||||
*
|
||||
* @param mongo
|
||||
* @param databaseName
|
||||
* @param defaultCollectionName
|
||||
* @param mongoConverter
|
||||
* @param writeConcern
|
||||
* @param writeResultChecking
|
||||
@@ -541,7 +540,7 @@ public class MongoTemplate implements MongoOperations, ApplicationEventPublisher
|
||||
|
||||
MongoPersistentEntity<?> entity = mappingContext.getPersistentEntity(o.getClass());
|
||||
if (entity == null) {
|
||||
throw new InvalidDataAccessApiUsageException("No Persitent Entity information found for the class " +
|
||||
throw new InvalidDataAccessApiUsageException("No Persitent Entity information found for the class " +
|
||||
o.getClass().getName());
|
||||
}
|
||||
String collection = entity.getCollection();
|
||||
@@ -721,59 +720,79 @@ public class MongoTemplate implements MongoOperations, ApplicationEventPublisher
|
||||
* @see org.springframework.data.document.mongodb.MongoOperations#updateFirst(com.mongodb.DBObject, com.mongodb.DBObject)
|
||||
*/
|
||||
public WriteResult updateFirst(Class<?> entityClass, Query query, Update update) {
|
||||
return updateFirst(determineCollectionName(entityClass), query, update);
|
||||
return doUpdate(determineCollectionName(entityClass), query, update, entityClass, false, false);
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.data.document.mongodb.MongoOperations#updateFirst(java.lang.String, com.mongodb.DBObject, com.mongodb.DBObject)
|
||||
*/
|
||||
public WriteResult updateFirst(String collectionName, final Query query, final Update update) {
|
||||
if (LOGGER.isDebugEnabled()) {
|
||||
LOGGER.debug("calling update using query: " + query.getQueryObject() + " and update: " + update.getUpdateObject(mongoConverter) + " in collection: " + collectionName);
|
||||
}
|
||||
return execute(collectionName, new CollectionCallback<WriteResult>() {
|
||||
public WriteResult doInCollection(DBCollection collection) throws MongoException, DataAccessException {
|
||||
DBObject updateObj = update.getUpdateObject(mongoConverter);
|
||||
|
||||
WriteResult wr;
|
||||
if (writeConcern == null) {
|
||||
wr = collection.update(query.getQueryObject(), updateObj);
|
||||
} else {
|
||||
wr = collection.update(query.getQueryObject(), updateObj, false, false, writeConcern);
|
||||
}
|
||||
handleAnyWriteResultErrors(wr, query.getQueryObject(), "update with '" + updateObj + "'");
|
||||
return wr;
|
||||
}
|
||||
});
|
||||
public WriteResult updateFirst(final String collectionName, final Query query, final Update update) {
|
||||
return doUpdate(collectionName, query, update, null, false, false);
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.data.document.mongodb.MongoOperations#updateMulti(com.mongodb.DBObject, com.mongodb.DBObject)
|
||||
*/
|
||||
public WriteResult updateMulti(Class<?> entityClass, Query query, Update update) {
|
||||
return updateMulti(determineCollectionName(entityClass), query, update);
|
||||
return doUpdate(determineCollectionName(entityClass), query, update, entityClass, false, true);
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.data.document.mongodb.MongoOperations#updateMulti(java.lang.String, com.mongodb.DBObject, com.mongodb.DBObject)
|
||||
*/
|
||||
public WriteResult updateMulti(String collectionName, final Query query, final Update update) {
|
||||
if (LOGGER.isDebugEnabled()) {
|
||||
LOGGER.debug("calling updateMulti using query: " + query.getQueryObject() + " and update: " + update.getUpdateObject(mongoConverter) + " in collection: " + collectionName);
|
||||
}
|
||||
return doUpdate(collectionName, query, update, null, false, true);
|
||||
}
|
||||
|
||||
private WriteResult doUpdate(final String collectionName,
|
||||
final Query query,
|
||||
final Update update,
|
||||
final Class<?> entityClass,
|
||||
final boolean upsert,
|
||||
final boolean multi) {
|
||||
|
||||
return execute(collectionName, new CollectionCallback<WriteResult>() {
|
||||
public WriteResult doInCollection(DBCollection collection) throws MongoException, DataAccessException {
|
||||
DBObject updateObj = update.getUpdateObject(mongoConverter);
|
||||
DBObject queryObj = query.getQueryObject();
|
||||
DBObject updateObj = update.getUpdateObject();
|
||||
|
||||
String idProperty = "id";
|
||||
if (null != entityClass) {
|
||||
idProperty = getPersistentEntity(entityClass).getIdProperty().getName();
|
||||
}
|
||||
for (String key : queryObj.keySet()) {
|
||||
if (idProperty.equals(key)) {
|
||||
// This is an ID field
|
||||
queryObj.put(ID, mongoConverter.maybeConvertObject(queryObj.get(key)));
|
||||
queryObj.removeField(key);
|
||||
} else {
|
||||
queryObj.put(key, mongoConverter.maybeConvertObject(queryObj.get(key)));
|
||||
}
|
||||
}
|
||||
|
||||
for (String key : updateObj.keySet()) {
|
||||
updateObj.put(key, mongoConverter.maybeConvertObject(updateObj.get(key)));
|
||||
}
|
||||
|
||||
if (LOGGER.isDebugEnabled()) {
|
||||
LOGGER.debug("calling update using query: " + queryObj + " and update: " + updateObj + " in collection: " + collectionName);
|
||||
}
|
||||
|
||||
WriteResult wr;
|
||||
if (writeConcern == null) {
|
||||
wr = collection.updateMulti(query.getQueryObject(), updateObj);
|
||||
if (multi) {
|
||||
wr = collection.updateMulti(queryObj, updateObj);
|
||||
} else {
|
||||
wr = collection.update(queryObj, updateObj);
|
||||
}
|
||||
} else {
|
||||
wr = collection.update(query.getQueryObject(), updateObj, false, true, writeConcern);
|
||||
wr = collection.update(queryObj, updateObj, upsert, multi, writeConcern);
|
||||
}
|
||||
handleAnyWriteResultErrors(wr, query.getQueryObject(), "update with '" + updateObj + "'");
|
||||
handleAnyWriteResultErrors(wr, queryObj, "update with '" + updateObj + "'");
|
||||
return wr;
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
|
||||
@@ -0,0 +1,103 @@
|
||||
/*
|
||||
* Copyright (c) 2011 by the original author(s).
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.data.document.mongodb.convert;
|
||||
|
||||
import static org.springframework.data.mapping.MappingBeanHelper.isSimpleType;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import com.mongodb.BasicDBList;
|
||||
import com.mongodb.BasicDBObject;
|
||||
import com.mongodb.DBObject;
|
||||
|
||||
/**
|
||||
* @author Jon Brisbin <jbrisbin@vmware.com>
|
||||
*/
|
||||
public abstract class AbstractMongoConverter implements MongoConverter {
|
||||
|
||||
public Object maybeConvertObject(Object obj) {
|
||||
if (obj instanceof Enum<?>) {
|
||||
return ((Enum<?>) obj).name();
|
||||
}
|
||||
|
||||
if (null != obj && 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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -17,6 +17,7 @@
|
||||
package org.springframework.data.document.mongodb.convert;
|
||||
|
||||
import static org.springframework.data.document.mongodb.convert.ObjectIdConverters.*;
|
||||
import static org.springframework.data.mapping.MappingBeanHelper.*;
|
||||
|
||||
import java.lang.reflect.Array;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
@@ -75,7 +76,7 @@ import org.springframework.expression.spel.support.StandardEvaluationContext;
|
||||
* @author Jon Brisbin <jbrisbin@vmware.com>
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
public class MappingMongoConverter implements MongoConverter, ApplicationContextAware, InitializingBean {
|
||||
public class MappingMongoConverter extends AbstractMongoConverter implements ApplicationContextAware, InitializingBean {
|
||||
|
||||
private static final String CUSTOM_TYPE_KEY = "_class";
|
||||
@SuppressWarnings({"unchecked"})
|
||||
@@ -112,7 +113,7 @@ public class MappingMongoConverter implements MongoConverter, ApplicationContext
|
||||
if (null != converters) {
|
||||
for (Converter<?, ?> c : converters) {
|
||||
registerConverter(c);
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -130,20 +131,20 @@ public class MappingMongoConverter implements MongoConverter, ApplicationContext
|
||||
}
|
||||
conversionService.addConverter(converter);
|
||||
}
|
||||
|
||||
|
||||
private Class<?> getCustomTarget(Class<?> source, Class<?> expectedTargetType) {
|
||||
for (ConvertiblePair typePair : customTypeMapping) {
|
||||
if (typePair.getSourceType().isAssignableFrom(source)) {
|
||||
|
||||
Class<?> targetType = typePair.getTargetType();
|
||||
|
||||
if (targetType.equals(expectedTargetType) || expectedTargetType == null) {
|
||||
return targetType;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
for (ConvertiblePair typePair : customTypeMapping) {
|
||||
if (typePair.getSourceType().isAssignableFrom(source)) {
|
||||
|
||||
Class<?> targetType = typePair.getTargetType();
|
||||
|
||||
if (targetType.equals(expectedTargetType) || expectedTargetType == null) {
|
||||
return targetType;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public MappingContext<BasicMongoPersistentEntity<?>> getMappingContext() {
|
||||
@@ -174,16 +175,17 @@ public class MappingMongoConverter implements MongoConverter, ApplicationContext
|
||||
return conversionService.convert(id, ObjectId.class);
|
||||
}
|
||||
|
||||
|
||||
@SuppressWarnings({"unchecked", "rawtypes"})
|
||||
public <S extends Object> S read(Class<S> clazz, final DBObject dbo) {
|
||||
if (null == dbo) {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
Class<?> customTarget = getCustomTarget(clazz, DBObject.class);
|
||||
|
||||
|
||||
if (customTarget != null) {
|
||||
return conversionService.convert(dbo, clazz);
|
||||
return conversionService.convert(dbo, clazz);
|
||||
}
|
||||
|
||||
if ((clazz.isArray()
|
||||
@@ -230,7 +232,7 @@ public class MappingMongoConverter implements MongoConverter, ApplicationContext
|
||||
}
|
||||
|
||||
final List<String> ctorParamNames = new ArrayList<String>();
|
||||
final S instance = MappingBeanHelper.constructInstance(entity, new PreferredConstructor.ParameterValueProvider() {
|
||||
final S instance = constructInstance(entity, new PreferredConstructor.ParameterValueProvider() {
|
||||
public Object getParameterValue(PreferredConstructor.Parameter parameter) {
|
||||
String name = parameter.getName();
|
||||
Class<?> type = parameter.getType();
|
||||
@@ -260,7 +262,7 @@ public class MappingMongoConverter implements MongoConverter, ApplicationContext
|
||||
if (dbo.containsField("_id") && null != idProperty) {
|
||||
Object idObj = dbo.get("_id");
|
||||
try {
|
||||
MappingBeanHelper.setProperty(instance, idProperty, idObj, useFieldAccessOnly);
|
||||
setProperty(instance, idProperty, idObj, useFieldAccessOnly);
|
||||
} catch (IllegalAccessException e) {
|
||||
throw new MappingException(e.getMessage(), e);
|
||||
} catch (InvocationTargetException e) {
|
||||
@@ -277,7 +279,7 @@ public class MappingMongoConverter implements MongoConverter, ApplicationContext
|
||||
|
||||
Object obj = getValueInternal(prop, dbo, spelCtx, prop.getSpelExpression());
|
||||
try {
|
||||
MappingBeanHelper.setProperty(instance, prop, obj, useFieldAccessOnly);
|
||||
setProperty(instance, prop, obj, useFieldAccessOnly);
|
||||
} catch (IllegalAccessException e) {
|
||||
throw new MappingException(e.getMessage(), e);
|
||||
} catch (InvocationTargetException e) {
|
||||
@@ -292,7 +294,7 @@ public class MappingMongoConverter implements MongoConverter, ApplicationContext
|
||||
PersistentProperty inverseProp = association.getInverse();
|
||||
Object obj = getValueInternal(inverseProp, dbo, spelCtx, inverseProp.getSpelExpression());
|
||||
try {
|
||||
MappingBeanHelper.setProperty(instance, inverseProp, obj);
|
||||
setProperty(instance, inverseProp, obj);
|
||||
} catch (IllegalAccessException e) {
|
||||
throw new MappingException(e.getMessage(), e);
|
||||
} catch (InvocationTargetException e) {
|
||||
@@ -308,13 +310,13 @@ public class MappingMongoConverter implements MongoConverter, ApplicationContext
|
||||
if (null == obj) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
Class<?> customTarget = getCustomTarget(obj.getClass(), DBObject.class);
|
||||
|
||||
|
||||
if (customTarget != null) {
|
||||
DBObject result = conversionService.convert(obj, DBObject.class);
|
||||
dbo.putAll(result);
|
||||
return;
|
||||
DBObject result = conversionService.convert(obj, DBObject.class);
|
||||
dbo.putAll(result);
|
||||
return;
|
||||
}
|
||||
|
||||
PersistentEntity<?> entity = mappingContext.getPersistentEntity(obj.getClass());
|
||||
@@ -336,7 +338,7 @@ public class MappingMongoConverter implements MongoConverter, ApplicationContext
|
||||
if (!dbo.containsField("_id") && null != idProperty) {
|
||||
Object idObj;
|
||||
try {
|
||||
idObj = MappingBeanHelper.getProperty(obj, idProperty, Object.class, useFieldAccessOnly);
|
||||
idObj = getProperty(obj, idProperty, Object.class, useFieldAccessOnly);
|
||||
} catch (IllegalAccessException e) {
|
||||
throw new MappingException(e.getMessage(), e);
|
||||
} catch (InvocationTargetException e) {
|
||||
@@ -359,14 +361,14 @@ public class MappingMongoConverter implements MongoConverter, ApplicationContext
|
||||
Class<?> type = prop.getType();
|
||||
Object propertyObj;
|
||||
try {
|
||||
propertyObj = MappingBeanHelper.getProperty(obj, prop, type, useFieldAccessOnly);
|
||||
propertyObj = getProperty(obj, prop, type, useFieldAccessOnly);
|
||||
} catch (IllegalAccessException e) {
|
||||
throw new MappingException(e.getMessage(), e);
|
||||
} catch (InvocationTargetException e) {
|
||||
throw new MappingException(e.getMessage(), e);
|
||||
}
|
||||
if (null != propertyObj) {
|
||||
if (!MappingBeanHelper.isSimpleType(propertyObj.getClass())) {
|
||||
if (!isSimpleType(propertyObj.getClass())) {
|
||||
writePropertyInternal(prop, propertyObj, dbo);
|
||||
} else {
|
||||
dbo.put(name, propertyObj);
|
||||
@@ -381,7 +383,7 @@ public class MappingMongoConverter implements MongoConverter, ApplicationContext
|
||||
Class<?> type = inverseProp.getType();
|
||||
Object propertyObj;
|
||||
try {
|
||||
propertyObj = MappingBeanHelper.getProperty(obj, inverseProp, type, useFieldAccessOnly);
|
||||
propertyObj = getProperty(obj, inverseProp, type, useFieldAccessOnly);
|
||||
} catch (IllegalAccessException e) {
|
||||
throw new MappingException(e.getMessage(), e);
|
||||
} catch (InvocationTargetException e) {
|
||||
@@ -417,7 +419,7 @@ public class MappingMongoConverter implements MongoConverter, ApplicationContext
|
||||
conversionService.addConverter(BigIntegerToObjectIdConverter.INSTANCE);
|
||||
}
|
||||
|
||||
MappingBeanHelper.setConversionService(conversionService);
|
||||
setConversionService(conversionService);
|
||||
}
|
||||
|
||||
@SuppressWarnings({"unchecked"})
|
||||
@@ -442,7 +444,7 @@ public class MappingMongoConverter implements MongoConverter, ApplicationContext
|
||||
if (null != dbref) {
|
||||
DBRef dbRef = createDBRef(propObjItem, dbref);
|
||||
dbList.add(dbRef);
|
||||
} else if (type.isArray() && MappingBeanHelper.isSimpleType(prop.getComponentType())) {
|
||||
} else if (type.isArray() && isSimpleType(prop.getComponentType())) {
|
||||
dbList.add(propObjItem);
|
||||
} else if (propObjItem instanceof List) {
|
||||
List<?> propObjColl = (List<?>) propObjItem;
|
||||
@@ -450,7 +452,7 @@ public class MappingMongoConverter implements MongoConverter, ApplicationContext
|
||||
while (typeInfo.isCollectionLike()) {
|
||||
typeInfo = new ClassTypeInformation(typeInfo.getComponentType().getType());
|
||||
}
|
||||
if (MappingBeanHelper.isSimpleType(typeInfo.getType())) {
|
||||
if (isSimpleType(typeInfo.getType())) {
|
||||
dbList.add(propObjColl);
|
||||
} else {
|
||||
BasicDBList propNestedDbList = new BasicDBList();
|
||||
@@ -461,7 +463,7 @@ public class MappingMongoConverter implements MongoConverter, ApplicationContext
|
||||
}
|
||||
dbList.add(propNestedDbList);
|
||||
}
|
||||
} else if (MappingBeanHelper.isSimpleType(propObjItem.getClass())) {
|
||||
} else if (isSimpleType(propObjItem.getClass())) {
|
||||
dbList.add(propObjItem);
|
||||
} else {
|
||||
BasicDBObject propDbObj = new BasicDBObject();
|
||||
@@ -505,9 +507,9 @@ public class MappingMongoConverter implements MongoConverter, ApplicationContext
|
||||
for (Map.Entry<Object, Object> entry : obj.entrySet()) {
|
||||
Object key = entry.getKey();
|
||||
Object val = entry.getValue();
|
||||
if (MappingBeanHelper.isSimpleType(key.getClass())) {
|
||||
if (isSimpleType(key.getClass())) {
|
||||
String simpleKey = conversionService.convert(key, String.class);
|
||||
if (MappingBeanHelper.isSimpleType(val.getClass())) {
|
||||
if (isSimpleType(val.getClass())) {
|
||||
dbo.put(simpleKey, val);
|
||||
} else {
|
||||
DBObject newDbo = new BasicDBObject();
|
||||
@@ -538,7 +540,7 @@ public class MappingMongoConverter implements MongoConverter, ApplicationContext
|
||||
PersistentProperty idProperty = targetEntity.getIdProperty();
|
||||
ObjectId id = null;
|
||||
try {
|
||||
id = MappingBeanHelper.getProperty(target, idProperty, ObjectId.class, useFieldAccessOnly);
|
||||
id = getProperty(target, idProperty, ObjectId.class, useFieldAccessOnly);
|
||||
if (null == id) {
|
||||
throw new MappingException("Cannot create a reference to an object with a NULL id.");
|
||||
}
|
||||
@@ -564,27 +566,27 @@ public class MappingMongoConverter implements MongoConverter, ApplicationContext
|
||||
|
||||
@SuppressWarnings({"unchecked"})
|
||||
protected Object getValueInternal(PersistentProperty prop, DBObject dbo, StandardEvaluationContext ctx, String spelExpr) {
|
||||
|
||||
String name = prop.getName();
|
||||
Class<?> propertyType = prop.getType();
|
||||
|
||||
|
||||
String name = prop.getName();
|
||||
Class<?> propertyType = prop.getType();
|
||||
|
||||
Object o;
|
||||
if (null != spelExpr) {
|
||||
Expression x = spelExpressionParser.parseExpression(spelExpr);
|
||||
o = x.getValue(ctx);
|
||||
} else {
|
||||
Object dbObj = dbo.get(name);
|
||||
|
||||
|
||||
if (dbObj == null) {
|
||||
return null;
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
Class<?> customTarget = getCustomTarget(dbObj.getClass(), propertyType);
|
||||
|
||||
|
||||
if (customTarget != null) {
|
||||
return conversionService.convert(dbObj, propertyType);
|
||||
return conversionService.convert(dbObj, propertyType);
|
||||
}
|
||||
|
||||
|
||||
if (dbObj instanceof DBRef) {
|
||||
dbObj = ((DBRef) dbObj).fetch();
|
||||
}
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
*/
|
||||
package org.springframework.data.document.mongodb.convert;
|
||||
|
||||
import com.mongodb.BasicDBList;
|
||||
import org.bson.types.ObjectId;
|
||||
import org.springframework.data.document.mongodb.MongoReader;
|
||||
import org.springframework.data.document.mongodb.MongoWriter;
|
||||
@@ -43,4 +44,10 @@ public interface MongoConverter extends MongoWriter<Object>, MongoReader<Object>
|
||||
public ObjectId convertObjectId(Object id);
|
||||
|
||||
MappingContext getMappingContext();
|
||||
|
||||
Object maybeConvertObject(Object obj);
|
||||
|
||||
Object[] maybeConvertArray(Object[] src);
|
||||
|
||||
BasicDBList maybeConvertList(BasicDBList dbl);
|
||||
}
|
||||
|
||||
@@ -16,10 +16,26 @@
|
||||
package org.springframework.data.document.mongodb.convert;
|
||||
|
||||
import static org.springframework.data.document.mongodb.convert.ObjectIdConverters.*;
|
||||
import java.lang.reflect.*;
|
||||
|
||||
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.math.BigInteger;
|
||||
import java.util.*;
|
||||
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.Iterator;
|
||||
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;
|
||||
@@ -48,506 +64,506 @@ 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
|
||||
*/
|
||||
public class SimpleMongoConverter implements MongoConverter, InitializingBean {
|
||||
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;
|
||||
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);
|
||||
}
|
||||
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 GenericConversionService conversionService;
|
||||
private final MappingContext<? extends MongoPersistentEntity<?>> mappingContext;
|
||||
private final GenericConversionService conversionService;
|
||||
private final MappingContext<? extends MongoPersistentEntity<?>> mappingContext;
|
||||
|
||||
/**
|
||||
* Creates a {@link SimpleMongoConverter}.
|
||||
*/
|
||||
public SimpleMongoConverter() {
|
||||
this.conversionService = ConversionServiceFactory.createDefaultConversionService();
|
||||
this.conversionService.removeConvertible(Object.class, String.class);
|
||||
this.mappingContext = new SimpleMongoMappingContext();
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.data.document.mongodb.convert.MongoConverter#getMappingContext()
|
||||
*/
|
||||
public MappingContext<? extends MongoPersistentEntity<?>> getMappingContext() {
|
||||
return mappingContext;
|
||||
}
|
||||
/**
|
||||
* Creates a {@link SimpleMongoConverter}.
|
||||
*/
|
||||
public SimpleMongoConverter() {
|
||||
this.conversionService = ConversionServiceFactory.createDefaultConversionService();
|
||||
this.conversionService.removeConvertible(Object.class, String.class);
|
||||
this.mappingContext = new SimpleMongoMappingContext();
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes additional converters that handle {@link ObjectId} conversion. Will register converters for supported
|
||||
* id types if none are registered for those conversion already. {@link GenericConversionService} is configured.
|
||||
*/
|
||||
private void initializeConverters() {
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.data.document.mongodb.convert.MongoConverter#getMappingContext()
|
||||
*/
|
||||
public MappingContext<? extends MongoPersistentEntity<?>> getMappingContext() {
|
||||
return mappingContext;
|
||||
}
|
||||
|
||||
if (!conversionService.canConvert(ObjectId.class, String.class)) {
|
||||
conversionService.addConverter(ObjectIdToStringConverter.INSTANCE);
|
||||
}
|
||||
if (!conversionService.canConvert(String.class, ObjectId.class)) {
|
||||
conversionService.addConverter(StringToObjectIdConverter.INSTANCE);
|
||||
}
|
||||
if (!conversionService.canConvert(ObjectId.class, BigInteger.class)) {
|
||||
conversionService.addConverter(ObjectIdToBigIntegerConverter.INSTANCE);
|
||||
}
|
||||
if (!conversionService.canConvert(BigInteger.class, ObjectId.class)) {
|
||||
conversionService.addConverter(BigIntegerToObjectIdConverter.INSTANCE);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Initializes additional converters that handle {@link ObjectId} conversion. Will register converters for supported
|
||||
* id types if none are registered for those conversion already. {@link GenericConversionService} is configured.
|
||||
*/
|
||||
private void initializeConverters() {
|
||||
|
||||
/**
|
||||
* Add custom {@link Converter} or {@link ConverterFactory} instances to be used that will take presidence over
|
||||
* using object traversal to convert and object to/from DBObject
|
||||
*
|
||||
* @param converters
|
||||
*/
|
||||
public void setConverters(Set<?> converters) {
|
||||
for (Object converter : converters) {
|
||||
boolean added = false;
|
||||
if (converter instanceof Converter) {
|
||||
this.conversionService.addConverter((Converter<?, ?>) converter);
|
||||
added = true;
|
||||
}
|
||||
if (converter instanceof ConverterFactory) {
|
||||
this.conversionService.addConverterFactory((ConverterFactory<?, ?>) converter);
|
||||
added = true;
|
||||
}
|
||||
if (!added) {
|
||||
throw new IllegalArgumentException("Given set contains element that is neither Converter nor ConverterFactory!");
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!conversionService.canConvert(ObjectId.class, String.class)) {
|
||||
conversionService.addConverter(ObjectIdToStringConverter.INSTANCE);
|
||||
}
|
||||
if (!conversionService.canConvert(String.class, ObjectId.class)) {
|
||||
conversionService.addConverter(StringToObjectIdConverter.INSTANCE);
|
||||
}
|
||||
if (!conversionService.canConvert(ObjectId.class, BigInteger.class)) {
|
||||
conversionService.addConverter(ObjectIdToBigIntegerConverter.INSTANCE);
|
||||
}
|
||||
if (!conversionService.canConvert(BigInteger.class, ObjectId.class)) {
|
||||
conversionService.addConverter(BigIntegerToObjectIdConverter.INSTANCE);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see org.springframework.data.document.mongodb.MongoWriter#write(java.lang.Object, com.mongodb.DBObject)
|
||||
*/
|
||||
@SuppressWarnings("rawtypes")
|
||||
public void write(Object obj, DBObject dbo) {
|
||||
/**
|
||||
* Add custom {@link Converter} or {@link ConverterFactory} instances to be used that will take presidence over
|
||||
* using object traversal to convert and object to/from DBObject
|
||||
*
|
||||
* @param converters
|
||||
*/
|
||||
public void setConverters(Set<?> converters) {
|
||||
for (Object converter : converters) {
|
||||
boolean added = false;
|
||||
if (converter instanceof Converter) {
|
||||
this.conversionService.addConverter((Converter<?, ?>) converter);
|
||||
added = true;
|
||||
}
|
||||
if (converter instanceof ConverterFactory) {
|
||||
this.conversionService.addConverterFactory((ConverterFactory<?, ?>) converter);
|
||||
added = true;
|
||||
}
|
||||
if (!added) {
|
||||
throw new IllegalArgumentException("Given set contains element that is neither Converter nor ConverterFactory!");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
MongoBeanWrapper beanWrapper = createWrapper(obj, false);
|
||||
for (MongoPropertyDescriptor descriptor : beanWrapper.getDescriptors()) {
|
||||
if (descriptor.isMappable()) {
|
||||
Object value = beanWrapper.getValue(descriptor);
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see org.springframework.data.document.mongodb.MongoWriter#write(java.lang.Object, com.mongodb.DBObject)
|
||||
*/
|
||||
@SuppressWarnings("rawtypes")
|
||||
public void write(Object obj, DBObject dbo) {
|
||||
|
||||
if (value == null) {
|
||||
continue;
|
||||
}
|
||||
MongoBeanWrapper beanWrapper = createWrapper(obj, false);
|
||||
for (MongoPropertyDescriptor descriptor : beanWrapper.getDescriptors()) {
|
||||
if (descriptor.isMappable()) {
|
||||
Object value = beanWrapper.getValue(descriptor);
|
||||
|
||||
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.");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (value == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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) {
|
||||
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.");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!isSimpleType(value.getClass())) {
|
||||
writeCompoundValue(dbo, keyToUse, value);
|
||||
} else {
|
||||
dbo.put(keyToUse, value);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* 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) {
|
||||
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
if (!isSimpleType(value.getClass())) {
|
||||
writeCompoundValue(dbo, keyToUse, value);
|
||||
} else {
|
||||
dbo.put(keyToUse, value);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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;
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
|
||||
// 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()) {
|
||||
Class<?> customTargetType = getCustomTargetType(value);
|
||||
if (customTargetType != null) {
|
||||
dbo.put(keyToUse, conversionService.convert(value, customTargetType));
|
||||
return;
|
||||
}
|
||||
|
||||
Object entryValue = entry.getValue();
|
||||
String entryKey = entry.getKey();
|
||||
DBObject nestedDbo = new BasicDBObject();
|
||||
write(value, nestedDbo);
|
||||
dbo.put(keyToUse, nestedDbo);
|
||||
}
|
||||
|
||||
if (!isSimpleType(entryValue.getClass())) {
|
||||
writeCompoundValue(dboToPopulate, entryKey, entryValue);
|
||||
} else {
|
||||
dboToPopulate.put(entryKey, entryValue);
|
||||
}
|
||||
}
|
||||
dbo.put(mapKey, dboToPopulate);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* 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) {
|
||||
|
||||
/**
|
||||
* 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);
|
||||
}
|
||||
}
|
||||
for (Class<?> mongoType : MONGO_TYPES) {
|
||||
if (conversionService.canConvert(obj.getClass(), mongoType)) {
|
||||
return mongoType;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see org.springframework.data.document.mongodb.MongoReader#read(java.lang.Class, com.mongodb.DBObject)
|
||||
*/
|
||||
public <S> S read(Class<S> clazz, DBObject source) {
|
||||
/**
|
||||
* 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;
|
||||
|
||||
if (source == null) {
|
||||
return 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()) {
|
||||
|
||||
Assert.notNull(clazz, "Mapped class was not specified");
|
||||
S target = BeanUtils.instantiateClass(clazz);
|
||||
MongoBeanWrapper bw = new MongoBeanWrapper(target, conversionService, true);
|
||||
Object entryValue = entry.getValue();
|
||||
String entryKey = entry.getKey();
|
||||
|
||||
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.");
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!isSimpleType(entryValue.getClass())) {
|
||||
writeCompoundValue(dboToPopulate, entryKey, entryValue);
|
||||
} else {
|
||||
dboToPopulate.put(entryKey, entryValue);
|
||||
}
|
||||
}
|
||||
dbo.put(mapKey, dboToPopulate);
|
||||
}
|
||||
}
|
||||
|
||||
return target;
|
||||
}
|
||||
/**
|
||||
* 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);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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) {
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see org.springframework.data.document.mongodb.MongoReader#read(java.lang.Class, com.mongodb.DBObject)
|
||||
*/
|
||||
public <S> S read(Class<S> clazz, DBObject source) {
|
||||
|
||||
Class<?> targetCollectionType = descriptor.getPropertyType();
|
||||
boolean targetIsArray = targetCollectionType.isArray();
|
||||
if (source == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
Collection<Object> result = targetIsArray ? new ArrayList<Object>(values.size()) : CollectionFactory
|
||||
.createCollection(targetCollectionType, values.size());
|
||||
Assert.notNull(clazz, "Mapped class was not specified");
|
||||
S target = BeanUtils.instantiateClass(clazz);
|
||||
MongoBeanWrapper bw = new MongoBeanWrapper(target, conversionService, true);
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
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 result;
|
||||
}
|
||||
return target;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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) {
|
||||
/**
|
||||
* 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) {
|
||||
|
||||
Assert.isTrue(!pd.isCollection(), "Collections not supported!");
|
||||
Class<?> targetCollectionType = descriptor.getPropertyType();
|
||||
boolean targetIsArray = targetCollectionType.isArray();
|
||||
|
||||
if (pd.isMap()) {
|
||||
return readMap(pd, dbo, getGenericParameters(pd.getTypeToSet()).get(1));
|
||||
} else {
|
||||
return read(pd.getPropertyType(), dbo);
|
||||
}
|
||||
}
|
||||
@SuppressWarnings("unchecked")
|
||||
Collection<Object> result = targetIsArray ? new ArrayList<Object>(values.size()) : CollectionFactory
|
||||
.createCollection(targetCollectionType, values.size());
|
||||
|
||||
/**
|
||||
* 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>();
|
||||
}
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
protected static boolean isSimpleType(Class<?> propertyType) {
|
||||
if (propertyType == null) {
|
||||
return false;
|
||||
}
|
||||
if (propertyType.isArray()) {
|
||||
return isSimpleType(propertyType.getComponentType());
|
||||
}
|
||||
return SIMPLE_TYPES.contains(propertyType.getName());
|
||||
}
|
||||
/**
|
||||
* 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) {
|
||||
|
||||
/**
|
||||
* 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) {
|
||||
Assert.isTrue(!pd.isCollection(), "Collections not supported!");
|
||||
|
||||
return new MongoBeanWrapper(target, conversionService, fieldAccess);
|
||||
}
|
||||
if (pd.isMap()) {
|
||||
return readMap(pd, dbo, getGenericParameters(pd.getTypeToSet()).get(1));
|
||||
} else {
|
||||
return read(pd.getPropertyType(), dbo);
|
||||
}
|
||||
}
|
||||
|
||||
public List<Class<?>> getGenericParameters(Type genericParameterType) {
|
||||
/**
|
||||
* 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>();
|
||||
}
|
||||
|
||||
List<Class<?>> actualGenericParameterTypes = new ArrayList<Class<?>>();
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
protected static boolean isSimpleType(Class<?> propertyType) {
|
||||
if (propertyType == null) {
|
||||
return false;
|
||||
}
|
||||
if (propertyType.isArray()) {
|
||||
return isSimpleType(propertyType.getComponentType());
|
||||
}
|
||||
return SIMPLE_TYPES.contains(propertyType.getName());
|
||||
}
|
||||
|
||||
return actualGenericParameterTypes;
|
||||
}
|
||||
/**
|
||||
* 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) {
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.data.document.mongodb.convert.MongoConverter#convertObjectId(org.bson.types.ObjectId, java.lang.Class)
|
||||
*/
|
||||
public <T> T convertObjectId(ObjectId id, Class<T> targetType) {
|
||||
return conversionService.convert(id, targetType);
|
||||
}
|
||||
return new MongoBeanWrapper(target, conversionService, fieldAccess);
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.data.document.mongodb.convert.MongoConverter#convertObjectId(java.lang.Object)
|
||||
*/
|
||||
public ObjectId convertObjectId(Object id) {
|
||||
return conversionService.convert(id, ObjectId.class);
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.beans.factory.InitializingBean#afterPropertiesSet()
|
||||
*/
|
||||
public void afterPropertiesSet() {
|
||||
initializeConverters();
|
||||
}
|
||||
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.document.mongodb.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.document.mongodb.convert.MongoConverter#convertObjectId(java.lang.Object)
|
||||
*/
|
||||
public ObjectId convertObjectId(Object id) {
|
||||
return conversionService.convert(id, ObjectId.class);
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.beans.factory.InitializingBean#afterPropertiesSet()
|
||||
*/
|
||||
public void afterPropertiesSet() {
|
||||
initializeConverters();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,97 +20,92 @@ import java.util.Collections;
|
||||
import com.mongodb.BasicDBObject;
|
||||
import com.mongodb.DBObject;
|
||||
import com.mongodb.util.JSON;
|
||||
import org.springframework.data.document.mongodb.convert.MongoConverter;
|
||||
|
||||
public class BasicUpdate extends Update {
|
||||
|
||||
private DBObject updateObject = null;
|
||||
private DBObject updateObject = null;
|
||||
|
||||
public BasicUpdate(String updateString) {
|
||||
super();
|
||||
this.updateObject = (DBObject) JSON.parse(updateString);
|
||||
}
|
||||
public BasicUpdate(String updateString) {
|
||||
super();
|
||||
this.updateObject = (DBObject) JSON.parse(updateString);
|
||||
}
|
||||
|
||||
public BasicUpdate(DBObject updateObject) {
|
||||
super();
|
||||
this.updateObject = updateObject;
|
||||
}
|
||||
public BasicUpdate(DBObject updateObject) {
|
||||
super();
|
||||
this.updateObject = updateObject;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Update set(String key, Object value) {
|
||||
updateObject.put("$set", Collections.singletonMap(key, convertValueIfNecessary(value)));
|
||||
return this;
|
||||
}
|
||||
@Override
|
||||
public Update set(String key, Object value) {
|
||||
updateObject.put("$set", Collections.singletonMap(key, value));
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Update unset(String key) {
|
||||
updateObject.put("$unset", Collections.singletonMap(key, 1));
|
||||
return this;
|
||||
}
|
||||
@Override
|
||||
public Update unset(String key) {
|
||||
updateObject.put("$unset", Collections.singletonMap(key, 1));
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Update inc(String key, Number inc) {
|
||||
updateObject.put("$inc", Collections.singletonMap(key, inc));
|
||||
return this;
|
||||
}
|
||||
@Override
|
||||
public Update inc(String key, Number inc) {
|
||||
updateObject.put("$inc", Collections.singletonMap(key, inc));
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Update push(String key, Object value) {
|
||||
updateObject.put("$push", Collections.singletonMap(key, convertValueIfNecessary(value)));
|
||||
return this;
|
||||
}
|
||||
@Override
|
||||
public Update push(String key, Object value) {
|
||||
updateObject.put("$push", Collections.singletonMap(key, value));
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Update pushAll(String key, Object[] values) {
|
||||
Object[] convertedValues = new Object[values.length];
|
||||
for (int i = 0; i < values.length; i++) {
|
||||
convertedValues[i] = convertValueIfNecessary(values[i]);
|
||||
}
|
||||
DBObject keyValue = new BasicDBObject();
|
||||
keyValue.put(key, convertedValues);
|
||||
updateObject.put("$pushAll", keyValue);
|
||||
return this;
|
||||
}
|
||||
@Override
|
||||
public Update pushAll(String key, Object[] values) {
|
||||
DBObject keyValue = new BasicDBObject();
|
||||
keyValue.put(key, values);
|
||||
updateObject.put("$pushAll", keyValue);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Update addToSet(String key, Object value) {
|
||||
updateObject.put("$addToSet", Collections.singletonMap(key, convertValueIfNecessary(value)));
|
||||
return this;
|
||||
}
|
||||
@Override
|
||||
public Update addToSet(String key, Object value) {
|
||||
updateObject.put("$addToSet", Collections.singletonMap(key, value));
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Update pop(String key, Position pos) {
|
||||
updateObject.put("$pop", Collections.singletonMap(key, (pos == Position.FIRST ? -1 : 1)));
|
||||
return this;
|
||||
}
|
||||
@Override
|
||||
public Update pop(String key, Position pos) {
|
||||
updateObject.put("$pop", Collections.singletonMap(key, (pos == Position.FIRST ? -1 : 1)));
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Update pull(String key, Object value) {
|
||||
updateObject.put("$pull", Collections.singletonMap(key, convertValueIfNecessary(value)));
|
||||
return this;
|
||||
}
|
||||
@Override
|
||||
public Update pull(String key, Object value) {
|
||||
updateObject.put("$pull", Collections.singletonMap(key, value));
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Update pullAll(String key, Object[] values) {
|
||||
Object[] convertedValues = new Object[values.length];
|
||||
for (int i = 0; i < values.length; i++) {
|
||||
convertedValues[i] = convertValueIfNecessary(values[i]);
|
||||
}
|
||||
DBObject keyValue = new BasicDBObject();
|
||||
keyValue.put(key, convertedValues);
|
||||
updateObject.put("$pullAll", keyValue);
|
||||
return this;
|
||||
}
|
||||
@Override
|
||||
public Update pullAll(String key, Object[] values) {
|
||||
Object[] convertedValues = new Object[values.length];
|
||||
for (int i = 0; i < values.length; i++) {
|
||||
convertedValues[i] = values[i];
|
||||
}
|
||||
DBObject keyValue = new BasicDBObject();
|
||||
keyValue.put(key, convertedValues);
|
||||
updateObject.put("$pullAll", keyValue);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Update rename(String oldName, String newName) {
|
||||
updateObject.put("$rename", Collections.singletonMap(oldName, newName));
|
||||
return this;
|
||||
}
|
||||
@Override
|
||||
public Update rename(String oldName, String newName) {
|
||||
updateObject.put("$rename", Collections.singletonMap(oldName, newName));
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DBObject getUpdateObject() {
|
||||
return updateObject;
|
||||
}
|
||||
@Override
|
||||
public DBObject getUpdateObject() {
|
||||
return updateObject;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -30,371 +30,368 @@ import org.springframework.util.Assert;
|
||||
|
||||
public class Criteria implements CriteriaDefinition {
|
||||
|
||||
private String key;
|
||||
private String key;
|
||||
|
||||
private List<Criteria> criteriaChain;
|
||||
private List<Criteria> criteriaChain;
|
||||
|
||||
private LinkedHashMap<String, Object> criteria = new LinkedHashMap<String, Object>();
|
||||
private LinkedHashMap<String, Object> criteria = new LinkedHashMap<String, Object>();
|
||||
|
||||
private Object isValue = null;
|
||||
private Object isValue = null;
|
||||
|
||||
public Criteria(String key) {
|
||||
this.criteriaChain = new ArrayList<Criteria>();
|
||||
this.criteriaChain.add(this);
|
||||
this.key = key;
|
||||
}
|
||||
public Criteria(String key) {
|
||||
this.criteriaChain = new ArrayList<Criteria>();
|
||||
this.criteriaChain.add(this);
|
||||
this.key = key;
|
||||
}
|
||||
|
||||
protected Criteria(List<Criteria> criteriaChain, String key) {
|
||||
this.criteriaChain = criteriaChain;
|
||||
this.criteriaChain.add(this);
|
||||
this.key = key;
|
||||
}
|
||||
protected Criteria(List<Criteria> criteriaChain, String key) {
|
||||
this.criteriaChain = criteriaChain;
|
||||
this.criteriaChain.add(this);
|
||||
this.key = key;
|
||||
}
|
||||
|
||||
/**
|
||||
* Static factory method to create a Criteria using the provided key
|
||||
*
|
||||
* @param key
|
||||
* @return
|
||||
*/
|
||||
public static Criteria where(String key) {
|
||||
return new Criteria(key);
|
||||
}
|
||||
/**
|
||||
* Static factory method to create a Criteria using the provided key
|
||||
*
|
||||
* @param key
|
||||
* @return
|
||||
*/
|
||||
public static Criteria where(String key) {
|
||||
return new Criteria(key);
|
||||
}
|
||||
|
||||
public static Criteria whereId() {
|
||||
return new Criteria("id");
|
||||
}
|
||||
public static Criteria whereId() {
|
||||
return new Criteria("id");
|
||||
}
|
||||
|
||||
/**
|
||||
* Static factory method to create a Criteria using the provided key
|
||||
*
|
||||
* @param key
|
||||
* @return
|
||||
*/
|
||||
public Criteria and(String key) {
|
||||
return new Criteria(this.criteriaChain, key);
|
||||
}
|
||||
/**
|
||||
* Static factory method to create a Criteria using the provided key
|
||||
*
|
||||
* @param key
|
||||
* @return
|
||||
*/
|
||||
public Criteria and(String key) {
|
||||
return new Criteria(this.criteriaChain, key);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a criterion using the $is operator
|
||||
*
|
||||
* @param o
|
||||
* @return
|
||||
*/
|
||||
public Criteria is(Object o) {
|
||||
if (isValue != null) {
|
||||
throw new InvalidDocumentStoreApiUsageException("Multiple 'is' values declared.");
|
||||
}
|
||||
this.isValue = o;
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* Creates a criterion using the $is operator
|
||||
*
|
||||
* @param o
|
||||
* @return
|
||||
*/
|
||||
public Criteria is(Object o) {
|
||||
if (isValue != null) {
|
||||
throw new InvalidDocumentStoreApiUsageException("Multiple 'is' values declared.");
|
||||
}
|
||||
this.isValue = o;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a criterion using the $ne operator
|
||||
*
|
||||
* @param o
|
||||
* @return
|
||||
*/
|
||||
public Criteria ne(Object o) {
|
||||
criteria.put("$ne", o);
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* Creates a criterion using the $ne operator
|
||||
*
|
||||
* @param o
|
||||
* @return
|
||||
*/
|
||||
public Criteria ne(Object o) {
|
||||
criteria.put("$ne", o);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a criterion using the $lt operator
|
||||
*
|
||||
* @param o
|
||||
* @return
|
||||
*/
|
||||
public Criteria lt(Object o) {
|
||||
criteria.put("$lt", o);
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* Creates a criterion using the $lt operator
|
||||
*
|
||||
* @param o
|
||||
* @return
|
||||
*/
|
||||
public Criteria lt(Object o) {
|
||||
criteria.put("$lt", o);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a criterion using the $lte operator
|
||||
*
|
||||
* @param o
|
||||
* @return
|
||||
*/
|
||||
public Criteria lte(Object o) {
|
||||
criteria.put("$lte", o);
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* Creates a criterion using the $lte operator
|
||||
*
|
||||
* @param o
|
||||
* @return
|
||||
*/
|
||||
public Criteria lte(Object o) {
|
||||
criteria.put("$lte", o);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a criterion using the $gt operator
|
||||
*
|
||||
* @param o
|
||||
* @return
|
||||
*/
|
||||
public Criteria gt(Object o) {
|
||||
criteria.put("$gt", o);
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* Creates a criterion using the $gt operator
|
||||
*
|
||||
* @param o
|
||||
* @return
|
||||
*/
|
||||
public Criteria gt(Object o) {
|
||||
criteria.put("$gt", o);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a criterion using the $gte operator
|
||||
*
|
||||
* @param o
|
||||
* @return
|
||||
*/
|
||||
public Criteria gte(Object o) {
|
||||
criteria.put("$gte", o);
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* Creates a criterion using the $gte operator
|
||||
*
|
||||
* @param o
|
||||
* @return
|
||||
*/
|
||||
public Criteria gte(Object o) {
|
||||
criteria.put("$gte", o);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a criterion using the $in operator
|
||||
*
|
||||
* @param o
|
||||
* @return
|
||||
*/
|
||||
public Criteria in(Object... o) {
|
||||
criteria.put("$in", o);
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* Creates a criterion using the $in operator
|
||||
*
|
||||
* @param o
|
||||
* @return
|
||||
*/
|
||||
public Criteria in(Object... o) {
|
||||
criteria.put("$in", o);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a criterion using the $nin operator
|
||||
*
|
||||
* @param o
|
||||
* @return
|
||||
*/
|
||||
public Criteria nin(Object... o) {
|
||||
criteria.put("$nin", o);
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* Creates a criterion using the $nin operator
|
||||
*
|
||||
* @param o
|
||||
* @return
|
||||
*/
|
||||
public Criteria nin(Object... o) {
|
||||
criteria.put("$nin", o);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a criterion using the $mod operator
|
||||
*
|
||||
* @param value
|
||||
* @param remainder
|
||||
* @return
|
||||
*/
|
||||
public Criteria mod(Number value, Number remainder) {
|
||||
List<Object> l = new ArrayList<Object>();
|
||||
l.add(value);
|
||||
l.add(remainder);
|
||||
criteria.put("$mod", l);
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* Creates a criterion using the $mod operator
|
||||
*
|
||||
* @param value
|
||||
* @param remainder
|
||||
* @return
|
||||
*/
|
||||
public Criteria mod(Number value, Number remainder) {
|
||||
List<Object> l = new ArrayList<Object>();
|
||||
l.add(value);
|
||||
l.add(remainder);
|
||||
criteria.put("$mod", l);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a criterion using the $all operator
|
||||
*
|
||||
* @param o
|
||||
* @return
|
||||
*/
|
||||
public Criteria all(Object o) {
|
||||
criteria.put("$is", o);
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* Creates a criterion using the $all operator
|
||||
*
|
||||
* @param o
|
||||
* @return
|
||||
*/
|
||||
public Criteria all(Object o) {
|
||||
criteria.put("$is", o);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a criterion using the $size operator
|
||||
*
|
||||
* @param s
|
||||
* @return
|
||||
*/
|
||||
public Criteria size(int s) {
|
||||
criteria.put("$size", s);
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* Creates a criterion using the $size operator
|
||||
*
|
||||
* @param s
|
||||
* @return
|
||||
*/
|
||||
public Criteria size(int s) {
|
||||
criteria.put("$size", s);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a criterion using the $exists operator
|
||||
*
|
||||
* @param b
|
||||
* @return
|
||||
*/
|
||||
public Criteria exists(boolean b) {
|
||||
criteria.put("$exists", b);
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* Creates a criterion using the $exists operator
|
||||
*
|
||||
* @param b
|
||||
* @return
|
||||
*/
|
||||
public Criteria exists(boolean b) {
|
||||
criteria.put("$exists", b);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a criterion using the $type operator
|
||||
*
|
||||
* @param t
|
||||
* @return
|
||||
*/
|
||||
public Criteria type(int t) {
|
||||
criteria.put("$type", t);
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* Creates a criterion using the $type operator
|
||||
*
|
||||
* @param t
|
||||
* @return
|
||||
*/
|
||||
public Criteria type(int t) {
|
||||
criteria.put("$type", t);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a criterion using the $not meta operator which affects the clause directly following
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public Criteria not() {
|
||||
criteria.put("$not", null);
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* Creates a criterion using the $not meta operator which affects the clause directly following
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public Criteria not() {
|
||||
criteria.put("$not", null);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a criterion using a $regex
|
||||
*
|
||||
* @param re
|
||||
* @return
|
||||
*/
|
||||
public Criteria regex(String re) {
|
||||
criteria.put("$regex", re);
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* Creates a criterion using a $regex
|
||||
*
|
||||
* @param re
|
||||
* @return
|
||||
*/
|
||||
public Criteria regex(String re) {
|
||||
criteria.put("$regex", re);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a geospatial criterion using a $within $center operation
|
||||
* @param circle must not be {@literal null}
|
||||
* @return
|
||||
*/
|
||||
public Criteria withinCenter(Circle circle) {
|
||||
Assert.notNull(circle);
|
||||
LinkedList<Object> list = new LinkedList<Object>();
|
||||
list.addLast(circle.getCenter().asArray());
|
||||
list.add(circle.getRadius());
|
||||
criteria.put("$within", new BasicDBObject("$center", list));
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a geospatial criterion using a $within $center operation. This is only available for Mongo 1.7 and higher.
|
||||
* @param circle must not be {@literal null}
|
||||
* @return
|
||||
*/
|
||||
public Criteria withinCenterSphere(Circle circle) {
|
||||
Assert.notNull(circle);
|
||||
LinkedList<Object> list = new LinkedList<Object>();
|
||||
list.addLast(circle.getCenter().asArray());
|
||||
list.add(circle.getRadius());
|
||||
criteria.put("$within", new BasicDBObject("$centerSphere", list));
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a geospatial criterion using a $within $box operation
|
||||
*
|
||||
* @param circle
|
||||
* @return
|
||||
*/
|
||||
public Criteria withinBox(Box box){
|
||||
Assert.notNull(box);
|
||||
LinkedList<double[]> list = new LinkedList<double[]>();
|
||||
list.addLast(box.getLowerLeft().asArray());
|
||||
list.addLast(box.getUpperRight().asArray());
|
||||
criteria.put("$within", new BasicDBObject("$box", list));
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a geospatial criterion using a $near operation
|
||||
* @param point must not be {@literal null}
|
||||
* @return
|
||||
*/
|
||||
public Criteria near(Point point) {
|
||||
Assert.notNull(point);
|
||||
criteria.put("$near", point.asArray());
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* Creates a geospatial criterion using a $within $center operation
|
||||
*
|
||||
* @param circle must not be {@literal null}
|
||||
* @return
|
||||
*/
|
||||
public Criteria withinCenter(Circle circle) {
|
||||
Assert.notNull(circle);
|
||||
LinkedList<Object> list = new LinkedList<Object>();
|
||||
list.addLast(circle.getCenter().asArray());
|
||||
list.add(circle.getRadius());
|
||||
criteria.put("$within", new BasicDBObject("$center", list));
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a geospatial criterion using a $nearSphere operation. This is only available for Mongo 1.7 and higher.
|
||||
* @param point must not be {@literal null}
|
||||
* @return
|
||||
*/
|
||||
public Criteria nearSphere(Point point) {
|
||||
Assert.notNull(point);
|
||||
criteria.put("$nearSphere", point.asArray());
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* Creates a geospatial criterion using a $within $center operation. This is only available for Mongo 1.7 and higher.
|
||||
*
|
||||
* @param circle must not be {@literal null}
|
||||
* @return
|
||||
*/
|
||||
public Criteria withinCenterSphere(Circle circle) {
|
||||
Assert.notNull(circle);
|
||||
LinkedList<Object> list = new LinkedList<Object>();
|
||||
list.addLast(circle.getCenter().asArray());
|
||||
list.add(circle.getRadius());
|
||||
criteria.put("$within", new BasicDBObject("$centerSphere", list));
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a geospatical criterion using a $maxDistance operation, for use with $near
|
||||
*
|
||||
* @param maxDistance
|
||||
* @return
|
||||
*/
|
||||
public Criteria maxDistance(double maxDistance) {
|
||||
criteria.put("$maxDistance", maxDistance);
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* Creates a geospatial criterion using a $within $box operation
|
||||
*
|
||||
* @param box
|
||||
* @return
|
||||
*/
|
||||
public Criteria withinBox(Box box) {
|
||||
Assert.notNull(box);
|
||||
LinkedList<double[]> list = new LinkedList<double[]>();
|
||||
list.addLast(box.getLowerLeft().asArray());
|
||||
list.addLast(box.getUpperRight().asArray());
|
||||
criteria.put("$within", new BasicDBObject("$box", list));
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a criterion using the $elemMatch operator
|
||||
*
|
||||
* @param t
|
||||
* @return
|
||||
*/
|
||||
public Criteria elemMatch(Criteria c) {
|
||||
criteria.put("$elemMatch", c.getCriteriaObject());
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* Creates a geospatial criterion using a $near operation
|
||||
*
|
||||
* @param point must not be {@literal null}
|
||||
* @return
|
||||
*/
|
||||
public Criteria near(Point point) {
|
||||
Assert.notNull(point);
|
||||
criteria.put("$near", point.asArray());
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an or query using the $or operator for all of the provided queries
|
||||
*
|
||||
* @param queries
|
||||
*/
|
||||
public void or(List<Query> queries) {
|
||||
criteria.put("$or", queries);
|
||||
}
|
||||
/**
|
||||
* Creates a geospatial criterion using a $nearSphere operation. This is only available for Mongo 1.7 and higher.
|
||||
*
|
||||
* @param point must not be {@literal null}
|
||||
* @return
|
||||
*/
|
||||
public Criteria nearSphere(Point point) {
|
||||
Assert.notNull(point);
|
||||
criteria.put("$nearSphere", point.asArray());
|
||||
return this;
|
||||
}
|
||||
|
||||
public String getKey() {
|
||||
return this.key;
|
||||
}
|
||||
/**
|
||||
* Creates a geospatical criterion using a $maxDistance operation, for use with $near
|
||||
*
|
||||
* @param maxDistance
|
||||
* @return
|
||||
*/
|
||||
public Criteria maxDistance(double maxDistance) {
|
||||
criteria.put("$maxDistance", maxDistance);
|
||||
return this;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see org.springframework.datastore.document.mongodb.query.Criteria#
|
||||
* getCriteriaObject(java.lang.String)
|
||||
*/
|
||||
public DBObject getCriteriaObject() {
|
||||
if (this.criteriaChain.size() == 1) {
|
||||
return criteriaChain.get(0).getSingleCriteriaObject();
|
||||
} else {
|
||||
DBObject criteriaObject = new BasicDBObject();
|
||||
for (Criteria c : this.criteriaChain) {
|
||||
criteriaObject.putAll(c.getSingleCriteriaObject());
|
||||
}
|
||||
return criteriaObject;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Creates a criterion using the $elemMatch operator
|
||||
*
|
||||
* @param c
|
||||
* @return
|
||||
*/
|
||||
public Criteria elemMatch(Criteria c) {
|
||||
criteria.put("$elemMatch", c.getCriteriaObject());
|
||||
return this;
|
||||
}
|
||||
|
||||
protected DBObject getSingleCriteriaObject() {
|
||||
DBObject dbo = new BasicDBObject();
|
||||
boolean not = false;
|
||||
for (String k : this.criteria.keySet()) {
|
||||
if (not) {
|
||||
DBObject notDbo = new BasicDBObject();
|
||||
notDbo.put(k, convertValueIfNecessary(this.criteria.get(k)));
|
||||
dbo.put("$not", notDbo);
|
||||
not = false;
|
||||
} else {
|
||||
if ("$not".equals(k)) {
|
||||
not = true;
|
||||
} else {
|
||||
dbo.put(k, convertValueIfNecessary(this.criteria.get(k)));
|
||||
}
|
||||
}
|
||||
}
|
||||
DBObject queryCriteria = new BasicDBObject();
|
||||
if (isValue != null) {
|
||||
queryCriteria.put(this.key, convertValueIfNecessary(this.isValue));
|
||||
queryCriteria.putAll(dbo);
|
||||
} else {
|
||||
queryCriteria.put(this.key, dbo);
|
||||
}
|
||||
return queryCriteria;
|
||||
}
|
||||
/**
|
||||
* Creates an or query using the $or operator for all of the provided queries
|
||||
*
|
||||
* @param queries
|
||||
*/
|
||||
public void or(List<Query> queries) {
|
||||
criteria.put("$or", queries);
|
||||
}
|
||||
|
||||
private Object convertValueIfNecessary(Object value) {
|
||||
if (value instanceof Enum) {
|
||||
return ((Enum<?>) value).name();
|
||||
}
|
||||
return value;
|
||||
}
|
||||
public String getKey() {
|
||||
return this.key;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see org.springframework.datastore.document.mongodb.query.Criteria#
|
||||
* getCriteriaObject(java.lang.String)
|
||||
*/
|
||||
public DBObject getCriteriaObject() {
|
||||
if (this.criteriaChain.size() == 1) {
|
||||
return criteriaChain.get(0).getSingleCriteriaObject();
|
||||
} else {
|
||||
DBObject criteriaObject = new BasicDBObject();
|
||||
for (Criteria c : this.criteriaChain) {
|
||||
criteriaObject.putAll(c.getSingleCriteriaObject());
|
||||
}
|
||||
return criteriaObject;
|
||||
}
|
||||
}
|
||||
|
||||
protected DBObject getSingleCriteriaObject() {
|
||||
DBObject dbo = new BasicDBObject();
|
||||
boolean not = false;
|
||||
for (String k : this.criteria.keySet()) {
|
||||
if (not) {
|
||||
DBObject notDbo = new BasicDBObject();
|
||||
notDbo.put(k, this.criteria.get(k));
|
||||
dbo.put("$not", notDbo);
|
||||
not = false;
|
||||
} else {
|
||||
if ("$not".equals(k)) {
|
||||
not = true;
|
||||
} else {
|
||||
dbo.put(k, this.criteria.get(k));
|
||||
}
|
||||
}
|
||||
}
|
||||
DBObject queryCriteria = new BasicDBObject();
|
||||
if (isValue != null) {
|
||||
queryCriteria.put(this.key, this.isValue);
|
||||
queryCriteria.putAll(dbo);
|
||||
} else {
|
||||
queryCriteria.put(this.key, dbo);
|
||||
}
|
||||
return queryCriteria;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -16,16 +16,11 @@
|
||||
package org.springframework.data.document.mongodb.query;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import com.mongodb.BasicDBList;
|
||||
import com.mongodb.BasicDBObject;
|
||||
import com.mongodb.DBObject;
|
||||
import org.springframework.dao.InvalidDataAccessApiUsageException;
|
||||
import org.springframework.data.document.mongodb.convert.MongoConverter;
|
||||
import org.springframework.data.mapping.MappingBeanHelper;
|
||||
|
||||
public class Update {
|
||||
|
||||
@@ -53,7 +48,7 @@ public class Update {
|
||||
* @return
|
||||
*/
|
||||
public Update set(String key, Object value) {
|
||||
addMultiFieldOperation("$set", key, convertValueIfNecessary(value));
|
||||
addMultiFieldOperation("$set", key, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
@@ -88,7 +83,7 @@ public class Update {
|
||||
* @return
|
||||
*/
|
||||
public Update push(String key, Object value) {
|
||||
addMultiFieldOperation("$push", key, convertValueIfNecessary(value));
|
||||
addMultiFieldOperation("$push", key, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
@@ -102,7 +97,7 @@ public class Update {
|
||||
public Update pushAll(String key, Object[] values) {
|
||||
Object[] convertedValues = new Object[values.length];
|
||||
for (int i = 0; i < values.length; i++) {
|
||||
convertedValues[i] = convertValueIfNecessary(values[i]);
|
||||
convertedValues[i] = values[i];
|
||||
}
|
||||
DBObject keyValue = new BasicDBObject();
|
||||
keyValue.put(key, convertedValues);
|
||||
@@ -118,7 +113,7 @@ public class Update {
|
||||
* @return
|
||||
*/
|
||||
public Update addToSet(String key, Object value) {
|
||||
addMultiFieldOperation("$addToSet", key, convertValueIfNecessary(value));
|
||||
addMultiFieldOperation("$addToSet", key, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
@@ -143,7 +138,7 @@ public class Update {
|
||||
* @return
|
||||
*/
|
||||
public Update pull(String key, Object value) {
|
||||
addMultiFieldOperation("$pull", key, convertValueIfNecessary(value));
|
||||
addMultiFieldOperation("$pull", key, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
@@ -157,7 +152,7 @@ public class Update {
|
||||
public Update pullAll(String key, Object[] values) {
|
||||
Object[] convertedValues = new Object[values.length];
|
||||
for (int i = 0; i < values.length; i++) {
|
||||
convertedValues[i] = convertValueIfNecessary(values[i]);
|
||||
convertedValues[i] = values[i];
|
||||
}
|
||||
DBObject keyValue = new BasicDBObject();
|
||||
keyValue.put(key, convertedValues);
|
||||
@@ -177,23 +172,14 @@ public class Update {
|
||||
return this;
|
||||
}
|
||||
|
||||
public DBObject getUpdateObject(MongoConverter converter) {
|
||||
public DBObject getUpdateObject() {
|
||||
DBObject dbo = new BasicDBObject();
|
||||
for (String k : modifierOps.keySet()) {
|
||||
Object o = modifierOps.get(k);
|
||||
if (null != converter) {
|
||||
dbo.put(k, maybeConvertObject(o, converter));
|
||||
} else {
|
||||
dbo.put(k, o);
|
||||
}
|
||||
dbo.put(k, modifierOps.get(k));
|
||||
}
|
||||
return dbo;
|
||||
}
|
||||
|
||||
public DBObject getUpdateObject() {
|
||||
return getUpdateObject(null);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
protected void addMultiFieldOperation(String operator, String key,
|
||||
Object value) {
|
||||
@@ -213,54 +199,4 @@ public class Update {
|
||||
keyValueMap.put(key, value);
|
||||
}
|
||||
|
||||
protected Object convertValueIfNecessary(Object value) {
|
||||
if (value instanceof Enum) {
|
||||
return ((Enum<?>) value).name();
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
@SuppressWarnings({"unchecked"})
|
||||
protected Object maybeConvertObject(Object obj, MongoConverter converter) {
|
||||
if (null != obj && MappingBeanHelper.isSimpleType(obj.getClass())) {
|
||||
// Doesn't need conversion
|
||||
return obj;
|
||||
}
|
||||
|
||||
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(), converter));
|
||||
}
|
||||
return m;
|
||||
}
|
||||
|
||||
if (obj instanceof BasicDBList) {
|
||||
return maybeConvertList((BasicDBList) obj, converter);
|
||||
}
|
||||
|
||||
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, converter));
|
||||
}
|
||||
return newValueDbo;
|
||||
}
|
||||
|
||||
DBObject newDbo = new BasicDBObject();
|
||||
converter.write(obj, newDbo);
|
||||
return newDbo;
|
||||
}
|
||||
|
||||
protected BasicDBList maybeConvertList(BasicDBList dbl, MongoConverter converter) {
|
||||
BasicDBList newDbl = new BasicDBList();
|
||||
Iterator iter = dbl.iterator();
|
||||
while (iter.hasNext()) {
|
||||
Object o = iter.next();
|
||||
newDbl.add(maybeConvertObject(o, converter));
|
||||
}
|
||||
return newDbl;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -21,6 +21,8 @@ import static org.junit.Assert.*;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import com.mongodb.BasicDBObject;
|
||||
import com.mongodb.DBObject;
|
||||
import org.bson.types.ObjectId;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
@@ -28,9 +30,7 @@ import org.junit.runner.RunWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.runners.MockitoJUnitRunner;
|
||||
import org.springframework.dao.DataAccessException;
|
||||
|
||||
import com.mongodb.BasicDBObject;
|
||||
import com.mongodb.DBObject;
|
||||
import org.springframework.data.document.mongodb.convert.AbstractMongoConverter;
|
||||
import org.springframework.data.document.mongodb.convert.MongoConverter;
|
||||
import org.springframework.data.mapping.model.MappingContext;
|
||||
import org.springframework.data.mapping.model.PersistentEntity;
|
||||
@@ -38,292 +38,293 @@ import org.springframework.data.mapping.model.PersistentEntity;
|
||||
/**
|
||||
* Abstract base class for unit tests to specify behaviour we expect from {@link MongoOperations}. Subclasses return
|
||||
* instances of their implementation and thus can see if it correctly implements the {@link MongoOperations} interface.
|
||||
*
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
@RunWith(MockitoJUnitRunner.class)
|
||||
public abstract class MongoOperationsUnitTests {
|
||||
|
||||
@Mock
|
||||
CollectionCallback<Object> collectionCallback;
|
||||
@Mock
|
||||
DbCallback<Object> dbCallback;
|
||||
@Mock
|
||||
CollectionCallback<Object> collectionCallback;
|
||||
@Mock
|
||||
DbCallback<Object> dbCallback;
|
||||
|
||||
MongoConverter converter;
|
||||
Person person;
|
||||
List<Person> persons;
|
||||
MongoConverter converter;
|
||||
Person person;
|
||||
List<Person> persons;
|
||||
|
||||
@Before
|
||||
public final void operationsSetUp() {
|
||||
@Before
|
||||
public final void operationsSetUp() {
|
||||
|
||||
person = new Person("Oliver");
|
||||
persons = Arrays.asList(person);
|
||||
person = new Person("Oliver");
|
||||
persons = Arrays.asList(person);
|
||||
|
||||
converter = new MongoConverter() {
|
||||
converter = new AbstractMongoConverter() {
|
||||
|
||||
public void write(Object t, DBObject dbo) {
|
||||
dbo.put("firstName", person.getFirstName());
|
||||
}
|
||||
public void write(Object t, DBObject dbo) {
|
||||
dbo.put("firstName", person.getFirstName());
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public <S extends Object> S read(Class<S> clazz, DBObject dbo) {
|
||||
return (S) person;
|
||||
}
|
||||
@SuppressWarnings("unchecked")
|
||||
public <S extends Object> S read(Class<S> clazz, DBObject dbo) {
|
||||
return (S) person;
|
||||
}
|
||||
|
||||
public <T> T convertObjectId(ObjectId id, Class<T> targetType) {
|
||||
return null;
|
||||
}
|
||||
public <T> T convertObjectId(ObjectId id, Class<T> targetType) {
|
||||
return null;
|
||||
}
|
||||
|
||||
public ObjectId convertObjectId(Object id) {
|
||||
return null;
|
||||
}
|
||||
|
||||
public MappingContext<PersistentEntity<?>> getMappingContext() {
|
||||
return null;
|
||||
}
|
||||
};
|
||||
}
|
||||
public ObjectId convertObjectId(Object id) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
@SuppressWarnings({ "unchecked", "rawtypes" })
|
||||
public void rejectsNullForCollectionCallback() {
|
||||
public MappingContext<PersistentEntity<?>> getMappingContext() {
|
||||
return null;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
getOperations().execute("test", (CollectionCallback) null);
|
||||
}
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
@SuppressWarnings({"unchecked", "rawtypes"})
|
||||
public void rejectsNullForCollectionCallback() {
|
||||
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
@SuppressWarnings({ "unchecked", "rawtypes" })
|
||||
public void rejectsNullForCollectionCallback2() {
|
||||
getOperations().execute("collection", (CollectionCallback) null);
|
||||
}
|
||||
getOperations().execute("test", (CollectionCallback) null);
|
||||
}
|
||||
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
@SuppressWarnings({ "unchecked", "rawtypes" })
|
||||
public void rejectsNullForDbCallback() {
|
||||
getOperations().execute((DbCallback) null);
|
||||
}
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
@SuppressWarnings({"unchecked", "rawtypes"})
|
||||
public void rejectsNullForCollectionCallback2() {
|
||||
getOperations().execute("collection", (CollectionCallback) null);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void convertsExceptionForCollectionExists() {
|
||||
new Execution() {
|
||||
@Override
|
||||
public void doWith(MongoOperations operations) {
|
||||
operations.collectionExists("foo");
|
||||
}
|
||||
}.assertDataAccessException();
|
||||
}
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
@SuppressWarnings({"unchecked", "rawtypes"})
|
||||
public void rejectsNullForDbCallback() {
|
||||
getOperations().execute((DbCallback) null);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void convertsExceptionForCreateCollection() {
|
||||
new Execution() {
|
||||
@Override
|
||||
public void doWith(MongoOperations operations) {
|
||||
operations.createCollection("foo");
|
||||
}
|
||||
}.assertDataAccessException();
|
||||
}
|
||||
@Test
|
||||
public void convertsExceptionForCollectionExists() {
|
||||
new Execution() {
|
||||
@Override
|
||||
public void doWith(MongoOperations operations) {
|
||||
operations.collectionExists("foo");
|
||||
}
|
||||
}.assertDataAccessException();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void convertsExceptionForCreateCollection2() {
|
||||
new Execution() {
|
||||
@Override
|
||||
public void doWith(MongoOperations operations) {
|
||||
operations.createCollection("foo", new CollectionOptions(1, 1, true));
|
||||
}
|
||||
}.assertDataAccessException();
|
||||
}
|
||||
@Test
|
||||
public void convertsExceptionForCreateCollection() {
|
||||
new Execution() {
|
||||
@Override
|
||||
public void doWith(MongoOperations operations) {
|
||||
operations.createCollection("foo");
|
||||
}
|
||||
}.assertDataAccessException();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void convertsExceptionForDropCollection() {
|
||||
new Execution() {
|
||||
@Override
|
||||
public void doWith(MongoOperations operations) {
|
||||
operations.dropCollection("foo");
|
||||
}
|
||||
}.assertDataAccessException();
|
||||
}
|
||||
@Test
|
||||
public void convertsExceptionForCreateCollection2() {
|
||||
new Execution() {
|
||||
@Override
|
||||
public void doWith(MongoOperations operations) {
|
||||
operations.createCollection("foo", new CollectionOptions(1, 1, true));
|
||||
}
|
||||
}.assertDataAccessException();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void convertsExceptionForExecuteCollectionCallback() {
|
||||
new Execution() {
|
||||
@Override
|
||||
public void doWith(MongoOperations operations) {
|
||||
operations.execute("test", collectionCallback);
|
||||
}
|
||||
}.assertDataAccessException();
|
||||
}
|
||||
@Test
|
||||
public void convertsExceptionForDropCollection() {
|
||||
new Execution() {
|
||||
@Override
|
||||
public void doWith(MongoOperations operations) {
|
||||
operations.dropCollection("foo");
|
||||
}
|
||||
}.assertDataAccessException();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void convertsExceptionForExecuteDbCallback() {
|
||||
new Execution() {
|
||||
@Override
|
||||
public void doWith(MongoOperations operations) {
|
||||
operations.execute(dbCallback);
|
||||
}
|
||||
}.assertDataAccessException();
|
||||
}
|
||||
@Test
|
||||
public void convertsExceptionForExecuteCollectionCallback() {
|
||||
new Execution() {
|
||||
@Override
|
||||
public void doWith(MongoOperations operations) {
|
||||
operations.execute("test", collectionCallback);
|
||||
}
|
||||
}.assertDataAccessException();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void convertsExceptionForExecuteCollectionCallbackAndCollection() {
|
||||
new Execution() {
|
||||
@Override
|
||||
public void doWith(MongoOperations operations) {
|
||||
operations.execute("collection", collectionCallback);
|
||||
}
|
||||
}.assertDataAccessException();
|
||||
}
|
||||
@Test
|
||||
public void convertsExceptionForExecuteDbCallback() {
|
||||
new Execution() {
|
||||
@Override
|
||||
public void doWith(MongoOperations operations) {
|
||||
operations.execute(dbCallback);
|
||||
}
|
||||
}.assertDataAccessException();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void convertsExceptionForExecuteCommand() {
|
||||
new Execution() {
|
||||
@Override
|
||||
public void doWith(MongoOperations operations) {
|
||||
operations.executeCommand(new BasicDBObject());
|
||||
}
|
||||
}.assertDataAccessException();
|
||||
}
|
||||
@Test
|
||||
public void convertsExceptionForExecuteCollectionCallbackAndCollection() {
|
||||
new Execution() {
|
||||
@Override
|
||||
public void doWith(MongoOperations operations) {
|
||||
operations.execute("collection", collectionCallback);
|
||||
}
|
||||
}.assertDataAccessException();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void convertsExceptionForExecuteStringCommand() {
|
||||
new Execution() {
|
||||
@Override
|
||||
public void doWith(MongoOperations operations) {
|
||||
operations.executeCommand("");
|
||||
}
|
||||
}.assertDataAccessException();
|
||||
}
|
||||
@Test
|
||||
public void convertsExceptionForExecuteCommand() {
|
||||
new Execution() {
|
||||
@Override
|
||||
public void doWith(MongoOperations operations) {
|
||||
operations.executeCommand(new BasicDBObject());
|
||||
}
|
||||
}.assertDataAccessException();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void convertsExceptionForExecuteInSession() {
|
||||
new Execution() {
|
||||
@Override
|
||||
public void doWith(MongoOperations operations) {
|
||||
operations.executeInSession(dbCallback);
|
||||
}
|
||||
}.assertDataAccessException();
|
||||
}
|
||||
@Test
|
||||
public void convertsExceptionForExecuteStringCommand() {
|
||||
new Execution() {
|
||||
@Override
|
||||
public void doWith(MongoOperations operations) {
|
||||
operations.executeCommand("");
|
||||
}
|
||||
}.assertDataAccessException();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void convertsExceptionForGetCollection() {
|
||||
new Execution() {
|
||||
@Override
|
||||
public void doWith(MongoOperations operations) {
|
||||
operations.getCollection(Object.class);
|
||||
}
|
||||
}.assertDataAccessException();
|
||||
}
|
||||
@Test
|
||||
public void convertsExceptionForExecuteInSession() {
|
||||
new Execution() {
|
||||
@Override
|
||||
public void doWith(MongoOperations operations) {
|
||||
operations.executeInSession(dbCallback);
|
||||
}
|
||||
}.assertDataAccessException();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void convertsExceptionForGetCollectionWithCollectionName() {
|
||||
new Execution() {
|
||||
@Override
|
||||
public void doWith(MongoOperations operations) {
|
||||
operations.getCollection("collection");
|
||||
}
|
||||
}.assertDataAccessException();
|
||||
}
|
||||
@Test
|
||||
public void convertsExceptionForGetCollection() {
|
||||
new Execution() {
|
||||
@Override
|
||||
public void doWith(MongoOperations operations) {
|
||||
operations.getCollection(Object.class);
|
||||
}
|
||||
}.assertDataAccessException();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void convertsExceptionForGetCollectionWithCollectionNameAndType() {
|
||||
new Execution() {
|
||||
@Override
|
||||
public void doWith(MongoOperations operations) {
|
||||
operations.getCollection("collection", Object.class);
|
||||
}
|
||||
}.assertDataAccessException();
|
||||
}
|
||||
@Test
|
||||
public void convertsExceptionForGetCollectionWithCollectionName() {
|
||||
new Execution() {
|
||||
@Override
|
||||
public void doWith(MongoOperations operations) {
|
||||
operations.getCollection("collection");
|
||||
}
|
||||
}.assertDataAccessException();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void convertsExceptionForGetCollectionWithCollectionNameTypeAndReader() {
|
||||
new Execution() {
|
||||
@Override
|
||||
public void doWith(MongoOperations operations) {
|
||||
operations.getCollection("collection", Object.class);
|
||||
}
|
||||
}.assertDataAccessException();
|
||||
}
|
||||
@Test
|
||||
public void convertsExceptionForGetCollectionWithCollectionNameAndType() {
|
||||
new Execution() {
|
||||
@Override
|
||||
public void doWith(MongoOperations operations) {
|
||||
operations.getCollection("collection", Object.class);
|
||||
}
|
||||
}.assertDataAccessException();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void convertsExceptionForGetCollectionNames() {
|
||||
new Execution() {
|
||||
@Override
|
||||
public void doWith(MongoOperations operations) {
|
||||
operations.getCollectionNames();
|
||||
}
|
||||
}.assertDataAccessException();
|
||||
}
|
||||
@Test
|
||||
public void convertsExceptionForGetCollectionWithCollectionNameTypeAndReader() {
|
||||
new Execution() {
|
||||
@Override
|
||||
public void doWith(MongoOperations operations) {
|
||||
operations.getCollection("collection", Object.class);
|
||||
}
|
||||
}.assertDataAccessException();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void convertsExceptionForInsert() {
|
||||
new Execution() {
|
||||
@Override
|
||||
public void doWith(MongoOperations operations) {
|
||||
operations.insert(person);
|
||||
}
|
||||
}.assertDataAccessException();
|
||||
}
|
||||
@Test
|
||||
public void convertsExceptionForGetCollectionNames() {
|
||||
new Execution() {
|
||||
@Override
|
||||
public void doWith(MongoOperations operations) {
|
||||
operations.getCollectionNames();
|
||||
}
|
||||
}.assertDataAccessException();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void convertsExceptionForInsert2() {
|
||||
new Execution() {
|
||||
@Override
|
||||
public void doWith(MongoOperations operations) {
|
||||
operations.insert("collection", person);
|
||||
}
|
||||
}.assertDataAccessException();
|
||||
}
|
||||
@Test
|
||||
public void convertsExceptionForInsert() {
|
||||
new Execution() {
|
||||
@Override
|
||||
public void doWith(MongoOperations operations) {
|
||||
operations.insert(person);
|
||||
}
|
||||
}.assertDataAccessException();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void convertsExceptionForInsertList() throws Exception {
|
||||
new Execution() {
|
||||
@Override
|
||||
public void doWith(MongoOperations operations) {
|
||||
operations.insertList(persons);
|
||||
}
|
||||
}.assertDataAccessException();
|
||||
}
|
||||
@Test
|
||||
public void convertsExceptionForInsert2() {
|
||||
new Execution() {
|
||||
@Override
|
||||
public void doWith(MongoOperations operations) {
|
||||
operations.insert("collection", person);
|
||||
}
|
||||
}.assertDataAccessException();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void convertsExceptionForGetInsertList2() throws Exception {
|
||||
new Execution() {
|
||||
@Override
|
||||
public void doWith(MongoOperations operations) {
|
||||
operations.insertList("collection", persons);
|
||||
}
|
||||
}.assertDataAccessException();
|
||||
}
|
||||
@Test
|
||||
public void convertsExceptionForInsertList() throws Exception {
|
||||
new Execution() {
|
||||
@Override
|
||||
public void doWith(MongoOperations operations) {
|
||||
operations.insertList(persons);
|
||||
}
|
||||
}.assertDataAccessException();
|
||||
}
|
||||
|
||||
private abstract class Execution {
|
||||
@Test
|
||||
public void convertsExceptionForGetInsertList2() throws Exception {
|
||||
new Execution() {
|
||||
@Override
|
||||
public void doWith(MongoOperations operations) {
|
||||
operations.insertList("collection", persons);
|
||||
}
|
||||
}.assertDataAccessException();
|
||||
}
|
||||
|
||||
public void assertDataAccessException() {
|
||||
assertException(DataAccessException.class);
|
||||
}
|
||||
private abstract class Execution {
|
||||
|
||||
public void assertException(Class<? extends Exception> exception) {
|
||||
public void assertDataAccessException() {
|
||||
assertException(DataAccessException.class);
|
||||
}
|
||||
|
||||
try {
|
||||
doWith(getOperationsForExceptionHandling());
|
||||
fail("Expected " + exception + " but completed without any!");
|
||||
} catch (Exception e) {
|
||||
assertTrue("Expected " + exception + " but got " + e, exception.isInstance(e));
|
||||
}
|
||||
}
|
||||
public void assertException(Class<? extends Exception> exception) {
|
||||
|
||||
public abstract void doWith(MongoOperations operations);
|
||||
}
|
||||
try {
|
||||
doWith(getOperationsForExceptionHandling());
|
||||
fail("Expected " + exception + " but completed without any!");
|
||||
} catch (Exception e) {
|
||||
assertTrue("Expected " + exception + " but got " + e, exception.isInstance(e));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Expects an {@link MongoOperations} instance that will be used to check that invoking methods on it will only cause
|
||||
* {@link DataAccessException}s.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
protected abstract MongoOperations getOperationsForExceptionHandling();
|
||||
public abstract void doWith(MongoOperations operations);
|
||||
}
|
||||
|
||||
/**
|
||||
* Expects an {@link MongoOperations} instance that will be used to check that invoking methods on it will only cause
|
||||
* {@link DataAccessException}s.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
protected abstract MongoOperations getOperationsForExceptionHandling();
|
||||
|
||||
/**
|
||||
* Returns a plain {@link MongoOperations}.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
protected abstract MongoOperations getOperations();
|
||||
|
||||
/**
|
||||
* Returns a plain {@link MongoOperations}.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
protected abstract MongoOperations getOperations();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user