DATAMONGO-326 - QueryMapper now delegates type conversion to MongoConverter.
QueryMapper now delegates to a MongoConverter instead of a plain ConversionService and invokes optional conversion on it. This optional conversion now removes type information from the created DBObject.
This commit is contained in:
@@ -25,6 +25,7 @@ import org.bson.types.ObjectId;
|
||||
import org.springframework.core.convert.ConversionException;
|
||||
import org.springframework.core.convert.ConversionService;
|
||||
import org.springframework.data.mapping.PersistentEntity;
|
||||
import org.springframework.data.mongodb.core.convert.MongoConverter;
|
||||
import org.springframework.data.mongodb.core.mapping.MongoPersistentEntity;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
@@ -40,15 +41,17 @@ import com.mongodb.DBObject;
|
||||
public class QueryMapper {
|
||||
|
||||
private final ConversionService conversionService;
|
||||
private final MongoConverter converter;
|
||||
|
||||
/**
|
||||
* Creates a new {@link QueryMapper} with the given {@link ConversionService}.
|
||||
* Creates a new {@link QueryMapper} with the given {@link MongoConverter}.
|
||||
*
|
||||
* @param conversionService must not be {@literal null}.
|
||||
* @param converter must not be {@literal null}.
|
||||
*/
|
||||
public QueryMapper(ConversionService conversionService) {
|
||||
Assert.notNull(conversionService);
|
||||
this.conversionService = conversionService;
|
||||
public QueryMapper(MongoConverter converter) {
|
||||
Assert.notNull(converter);
|
||||
this.conversionService = converter.getConversionService();
|
||||
this.converter = converter;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -105,7 +108,7 @@ public class QueryMapper {
|
||||
value = convertId(value);
|
||||
}
|
||||
|
||||
newDbo.put(newKey, value);
|
||||
newDbo.put(newKey, converter.convertToMongoType(value));
|
||||
}
|
||||
|
||||
return newDbo;
|
||||
|
||||
@@ -72,7 +72,7 @@ import com.mongodb.DBRef;
|
||||
* @author Oliver Gierke
|
||||
* @author Jon Brisbin
|
||||
*/
|
||||
public class MappingMongoConverter extends AbstractMongoConverter implements ApplicationContextAware, TypeKeyAware {
|
||||
public class MappingMongoConverter extends AbstractMongoConverter implements ApplicationContextAware {
|
||||
|
||||
@SuppressWarnings("rawtypes")
|
||||
private static final TypeInformation<Map> MAP_TYPE_INFORMATION = ClassTypeInformation.from(Map.class);
|
||||
@@ -110,7 +110,7 @@ public class MappingMongoConverter extends AbstractMongoConverter implements App
|
||||
this.mongoDbFactory = mongoDbFactory;
|
||||
this.mappingContext = mappingContext;
|
||||
this.typeMapper = new DefaultMongoTypeMapper(DefaultMongoTypeMapper.DEFAULT_TYPE_KEY, mappingContext);
|
||||
this.idMapper = new QueryMapper(conversionService);
|
||||
this.idMapper = new QueryMapper(this);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -126,14 +126,6 @@ public class MappingMongoConverter extends AbstractMongoConverter implements App
|
||||
mappingContext) : typeMapper;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.mongodb.core.convert.TypeKeyAware#isTypeKey(java.lang.String)
|
||||
*/
|
||||
public boolean isTypeKey(String key) {
|
||||
return typeMapper.isTypeKey(key);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.convert.EntityConverter#getMappingContext()
|
||||
@@ -908,7 +900,7 @@ public class MappingMongoConverter extends AbstractMongoConverter implements App
|
||||
|
||||
DBObject newDbo = new BasicDBObject();
|
||||
this.write(obj, newDbo);
|
||||
return newDbo;
|
||||
return removeTypeInfoRecursively(newDbo);
|
||||
}
|
||||
|
||||
public BasicDBList maybeConvertList(Iterable<?> source) {
|
||||
@@ -918,4 +910,41 @@ public class MappingMongoConverter extends AbstractMongoConverter implements App
|
||||
}
|
||||
return newDbl;
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the type information from the conversion result.
|
||||
*
|
||||
* @param object
|
||||
* @return
|
||||
*/
|
||||
private Object removeTypeInfoRecursively(Object object) {
|
||||
|
||||
if (!(object instanceof DBObject)) {
|
||||
return object;
|
||||
}
|
||||
|
||||
DBObject dbObject = (DBObject) object;
|
||||
String keyToRemove = null;
|
||||
for (String key : dbObject.keySet()) {
|
||||
|
||||
if (typeMapper.isTypeKey(key)) {
|
||||
keyToRemove = key;
|
||||
}
|
||||
|
||||
Object value = dbObject.get(key);
|
||||
if (value instanceof BasicDBList) {
|
||||
for (Object element : (BasicDBList) value) {
|
||||
removeTypeInfoRecursively(element);
|
||||
}
|
||||
} else {
|
||||
removeTypeInfoRecursively(value);
|
||||
}
|
||||
}
|
||||
|
||||
if (keyToRemove != null) {
|
||||
dbObject.removeField(keyToRemove);
|
||||
}
|
||||
|
||||
return dbObject;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,11 +20,16 @@ import org.springframework.data.convert.TypeMapper;
|
||||
import com.mongodb.DBObject;
|
||||
|
||||
/**
|
||||
* Combining interface to express Mongo specific {@link TypeMapper} implementations will be {@link TypeKeyAware} as
|
||||
* well.
|
||||
* Mongo-specific {@link TypeMapper} exposing that {@link DBObject}s might contain a type key.
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
public interface MongoTypeMapper extends TypeMapper<DBObject>, TypeKeyAware {
|
||||
public interface MongoTypeMapper extends TypeMapper<DBObject> {
|
||||
|
||||
/**
|
||||
* Returns whether the given key is the type key.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
boolean isTypeKey(String key);
|
||||
}
|
||||
|
||||
@@ -1,33 +0,0 @@
|
||||
/*
|
||||
* Copyright 2011 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.mongodb.core.convert;
|
||||
|
||||
import org.springframework.data.convert.TypeMapper;
|
||||
|
||||
/**
|
||||
* Interfaces for components being able to provide a {@link TypeMapper}.
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
public interface TypeKeyAware {
|
||||
|
||||
/**
|
||||
* Returns the {@link TypeMapper}.
|
||||
*
|
||||
* @return the {@link TypeMapper} or {@literal null} if none available.
|
||||
*/
|
||||
boolean isTypeKey(String key);
|
||||
}
|
||||
@@ -19,6 +19,7 @@ import java.math.BigInteger;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import org.bson.types.CodeWScope;
|
||||
import org.bson.types.ObjectId;
|
||||
@@ -48,6 +49,7 @@ public abstract class MongoSimpleTypes {
|
||||
simpleTypes.add(ObjectId.class);
|
||||
simpleTypes.add(CodeWScope.class);
|
||||
simpleTypes.add(DBObject.class);
|
||||
simpleTypes.add(Pattern.class);
|
||||
MONGO_SIMPLE_TYPES = Collections.unmodifiableSet(simpleTypes);
|
||||
}
|
||||
|
||||
|
||||
@@ -20,15 +20,11 @@ import java.util.Iterator;
|
||||
import org.springframework.data.domain.Pageable;
|
||||
import org.springframework.data.domain.Sort;
|
||||
import org.springframework.data.mongodb.core.convert.MongoWriter;
|
||||
import org.springframework.data.mongodb.core.convert.TypeKeyAware;
|
||||
import org.springframework.data.mongodb.core.geo.Distance;
|
||||
import org.springframework.data.mongodb.core.geo.Point;
|
||||
import org.springframework.data.repository.query.ParameterAccessor;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
import com.mongodb.BasicDBList;
|
||||
import com.mongodb.DBObject;
|
||||
|
||||
/**
|
||||
* Custom {@link ParameterAccessor} that uses a {@link MongoWriter} to serialize parameters into Mongo format.
|
||||
*
|
||||
@@ -111,49 +107,7 @@ public class ConvertingParameterAccessor implements MongoParameterAccessor {
|
||||
* @return
|
||||
*/
|
||||
private Object getConvertedValue(Object value) {
|
||||
|
||||
if (!(writer instanceof TypeKeyAware)) {
|
||||
return value;
|
||||
}
|
||||
|
||||
return removeTypeInfoRecursively(writer.convertToMongoType(value), ((TypeKeyAware) writer));
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the type information from the conversion result.
|
||||
*
|
||||
* @param object
|
||||
* @return
|
||||
*/
|
||||
private Object removeTypeInfoRecursively(Object object, TypeKeyAware typeKeyAware) {
|
||||
|
||||
if (!(object instanceof DBObject) || typeKeyAware == null) {
|
||||
return object;
|
||||
}
|
||||
|
||||
DBObject dbObject = (DBObject) object;
|
||||
String keyToRemove = null;
|
||||
for (String key : dbObject.keySet()) {
|
||||
|
||||
if (typeKeyAware.isTypeKey(key)) {
|
||||
keyToRemove = key;
|
||||
}
|
||||
|
||||
Object value = dbObject.get(key);
|
||||
if (value instanceof BasicDBList) {
|
||||
for (Object element : (BasicDBList) value) {
|
||||
removeTypeInfoRecursively(element, typeKeyAware);
|
||||
}
|
||||
} else {
|
||||
removeTypeInfoRecursively(value, typeKeyAware);
|
||||
}
|
||||
}
|
||||
|
||||
if (keyToRemove != null) {
|
||||
dbObject.removeField(keyToRemove);
|
||||
}
|
||||
|
||||
return dbObject;
|
||||
return writer.convertToMongoType(value);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -68,6 +68,7 @@ public class MongoNamespaceTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("deprecation")
|
||||
public void testMongoSingletonWithPropertyPlaceHolders() throws Exception {
|
||||
assertTrue(ctx.containsBean("mongo"));
|
||||
MongoFactoryBean mfb = (MongoFactoryBean) ctx.getBean("&mongo");
|
||||
|
||||
@@ -17,6 +17,8 @@ package org.springframework.data.mongodb.core.query;
|
||||
|
||||
import static org.hamcrest.Matchers.*;
|
||||
import static org.junit.Assert.*;
|
||||
import static org.springframework.data.mongodb.core.query.Query.*;
|
||||
import static org.springframework.data.mongodb.core.query.Criteria.*;
|
||||
|
||||
import java.math.BigInteger;
|
||||
|
||||
@@ -59,7 +61,7 @@ public class QueryMapperUnitTests {
|
||||
MappingMongoConverter converter = new MappingMongoConverter(factory, context);
|
||||
converter.afterPropertiesSet();
|
||||
|
||||
mapper = new QueryMapper(converter.getConversionService());
|
||||
mapper = new QueryMapper(converter);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -90,12 +92,12 @@ public class QueryMapperUnitTests {
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATADOC-278
|
||||
* @see DATAMONGO-278
|
||||
*/
|
||||
@Test
|
||||
public void translates$NeCorrectly() {
|
||||
|
||||
Criteria criteria = Criteria.where("foo").ne(new ObjectId().toString());
|
||||
Criteria criteria = where("foo").ne(new ObjectId().toString());
|
||||
|
||||
DBObject result = mapper.getMappedObject(criteria.getCriteriaObject(), context.getPersistentEntity(Sample.class));
|
||||
Object object = result.get("_id");
|
||||
@@ -104,6 +106,18 @@ public class QueryMapperUnitTests {
|
||||
assertThat(dbObject.get("$ne"), is(ObjectId.class));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-326
|
||||
*/
|
||||
@Test
|
||||
public void handlesEnumsCorrectly() {
|
||||
Query query = query(where("foo").is(Enum.INSTANCE));
|
||||
DBObject result = mapper.getMappedObject(query.getQueryObject(), null);
|
||||
|
||||
Object object = result.get("foo");
|
||||
assertThat(object, is(String.class));
|
||||
}
|
||||
|
||||
class Sample {
|
||||
|
||||
@Id
|
||||
@@ -115,4 +129,8 @@ public class QueryMapperUnitTests {
|
||||
@Id
|
||||
private BigInteger id;
|
||||
}
|
||||
|
||||
enum Enum {
|
||||
INSTANCE;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,7 +30,6 @@ import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.Mockito;
|
||||
import org.mockito.invocation.InvocationOnMock;
|
||||
import org.mockito.runners.MockitoJUnitRunner;
|
||||
import org.mockito.stubbing.Answer;
|
||||
@@ -50,9 +49,6 @@ import org.springframework.data.repository.Repository;
|
||||
import org.springframework.data.repository.core.support.DefaultRepositoryMetadata;
|
||||
import org.springframework.data.repository.query.parser.PartTree;
|
||||
|
||||
import com.mongodb.BasicDBObject;
|
||||
import com.mongodb.DBObject;
|
||||
|
||||
/**
|
||||
* Unit test for {@link MongoQueryCreator}.
|
||||
*
|
||||
@@ -72,14 +68,12 @@ public class MongoQueryCreatorUnitTests {
|
||||
public void setUp() throws SecurityException, NoSuchMethodException {
|
||||
|
||||
context = new MongoMappingContext();
|
||||
|
||||
doAnswer(new Answer<Void>() {
|
||||
public Void answer(InvocationOnMock invocation) throws Throwable {
|
||||
DBObject dbObject = (DBObject) invocation.getArguments()[1];
|
||||
dbObject.put("value", new BasicDBObject("value", "value"));
|
||||
return null;
|
||||
|
||||
doAnswer(new Answer<Object>() {
|
||||
public Object answer(InvocationOnMock invocation) throws Throwable {
|
||||
return invocation.getArguments()[0];
|
||||
}
|
||||
}).when(converter).write(any(), Mockito.any(DBObject.class));
|
||||
}).when(converter).convertToMongoType(any());
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
Reference in New Issue
Block a user