Browse Source

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.
pull/1/head
Oliver Gierke 14 years ago
parent
commit
c7f7571f3f
  1. 15
      spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/QueryMapper.java
  2. 51
      spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/MappingMongoConverter.java
  3. 11
      spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/MongoTypeMapper.java
  4. 33
      spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/TypeKeyAware.java
  5. 2
      spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/mapping/MongoSimpleTypes.java
  6. 48
      spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/ConvertingParameterAccessor.java
  7. 1
      spring-data-mongodb/src/test/java/org/springframework/data/mongodb/config/MongoNamespaceTests.java
  8. 24
      spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/query/QueryMapperUnitTests.java
  9. 14
      spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/query/MongoQueryCreatorUnitTests.java

15
spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/QueryMapper.java

@ -25,6 +25,7 @@ import org.bson.types.ObjectId;
import org.springframework.core.convert.ConversionException; import org.springframework.core.convert.ConversionException;
import org.springframework.core.convert.ConversionService; import org.springframework.core.convert.ConversionService;
import org.springframework.data.mapping.PersistentEntity; 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.data.mongodb.core.mapping.MongoPersistentEntity;
import org.springframework.util.Assert; import org.springframework.util.Assert;
@ -40,15 +41,17 @@ import com.mongodb.DBObject;
public class QueryMapper { public class QueryMapper {
private final ConversionService conversionService; 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) { public QueryMapper(MongoConverter converter) {
Assert.notNull(conversionService); Assert.notNull(converter);
this.conversionService = conversionService; this.conversionService = converter.getConversionService();
this.converter = converter;
} }
/** /**
@ -105,7 +108,7 @@ public class QueryMapper {
value = convertId(value); value = convertId(value);
} }
newDbo.put(newKey, value); newDbo.put(newKey, converter.convertToMongoType(value));
} }
return newDbo; return newDbo;

51
spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/MappingMongoConverter.java

@ -72,7 +72,7 @@ import com.mongodb.DBRef;
* @author Oliver Gierke * @author Oliver Gierke
* @author Jon Brisbin * @author Jon Brisbin
*/ */
public class MappingMongoConverter extends AbstractMongoConverter implements ApplicationContextAware, TypeKeyAware { public class MappingMongoConverter extends AbstractMongoConverter implements ApplicationContextAware {
@SuppressWarnings("rawtypes") @SuppressWarnings("rawtypes")
private static final TypeInformation<Map> MAP_TYPE_INFORMATION = ClassTypeInformation.from(Map.class); 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.mongoDbFactory = mongoDbFactory;
this.mappingContext = mappingContext; this.mappingContext = mappingContext;
this.typeMapper = new DefaultMongoTypeMapper(DefaultMongoTypeMapper.DEFAULT_TYPE_KEY, 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; 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) * (non-Javadoc)
* @see org.springframework.data.convert.EntityConverter#getMappingContext() * @see org.springframework.data.convert.EntityConverter#getMappingContext()
@ -908,7 +900,7 @@ public class MappingMongoConverter extends AbstractMongoConverter implements App
DBObject newDbo = new BasicDBObject(); DBObject newDbo = new BasicDBObject();
this.write(obj, newDbo); this.write(obj, newDbo);
return newDbo; return removeTypeInfoRecursively(newDbo);
} }
public BasicDBList maybeConvertList(Iterable<?> source) { public BasicDBList maybeConvertList(Iterable<?> source) {
@ -918,4 +910,41 @@ public class MappingMongoConverter extends AbstractMongoConverter implements App
} }
return newDbl; 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;
}
} }

11
spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/MongoTypeMapper.java

@ -20,11 +20,16 @@ import org.springframework.data.convert.TypeMapper;
import com.mongodb.DBObject; import com.mongodb.DBObject;
/** /**
* Combining interface to express Mongo specific {@link TypeMapper} implementations will be {@link TypeKeyAware} as * Mongo-specific {@link TypeMapper} exposing that {@link DBObject}s might contain a type key.
* well.
* *
* @author Oliver Gierke * @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);
} }

33
spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/TypeKeyAware.java

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

2
spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/mapping/MongoSimpleTypes.java

@ -19,6 +19,7 @@ import java.math.BigInteger;
import java.util.Collections; import java.util.Collections;
import java.util.HashSet; import java.util.HashSet;
import java.util.Set; import java.util.Set;
import java.util.regex.Pattern;
import org.bson.types.CodeWScope; import org.bson.types.CodeWScope;
import org.bson.types.ObjectId; import org.bson.types.ObjectId;
@ -48,6 +49,7 @@ public abstract class MongoSimpleTypes {
simpleTypes.add(ObjectId.class); simpleTypes.add(ObjectId.class);
simpleTypes.add(CodeWScope.class); simpleTypes.add(CodeWScope.class);
simpleTypes.add(DBObject.class); simpleTypes.add(DBObject.class);
simpleTypes.add(Pattern.class);
MONGO_SIMPLE_TYPES = Collections.unmodifiableSet(simpleTypes); MONGO_SIMPLE_TYPES = Collections.unmodifiableSet(simpleTypes);
} }

48
spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/ConvertingParameterAccessor.java

@ -20,15 +20,11 @@ import java.util.Iterator;
import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort; import org.springframework.data.domain.Sort;
import org.springframework.data.mongodb.core.convert.MongoWriter; 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.Distance;
import org.springframework.data.mongodb.core.geo.Point; import org.springframework.data.mongodb.core.geo.Point;
import org.springframework.data.repository.query.ParameterAccessor; import org.springframework.data.repository.query.ParameterAccessor;
import org.springframework.util.Assert; 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. * Custom {@link ParameterAccessor} that uses a {@link MongoWriter} to serialize parameters into Mongo format.
* *
@ -111,49 +107,7 @@ public class ConvertingParameterAccessor implements MongoParameterAccessor {
* @return * @return
*/ */
private Object getConvertedValue(Object value) { private Object getConvertedValue(Object value) {
return writer.convertToMongoType(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;
} }
/** /**

1
spring-data-mongodb/src/test/java/org/springframework/data/mongodb/config/MongoNamespaceTests.java

@ -68,6 +68,7 @@ public class MongoNamespaceTests {
} }
@Test @Test
@SuppressWarnings("deprecation")
public void testMongoSingletonWithPropertyPlaceHolders() throws Exception { public void testMongoSingletonWithPropertyPlaceHolders() throws Exception {
assertTrue(ctx.containsBean("mongo")); assertTrue(ctx.containsBean("mongo"));
MongoFactoryBean mfb = (MongoFactoryBean) ctx.getBean("&mongo"); MongoFactoryBean mfb = (MongoFactoryBean) ctx.getBean("&mongo");

24
spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/query/QueryMapperUnitTests.java

@ -17,6 +17,8 @@ package org.springframework.data.mongodb.core.query;
import static org.hamcrest.Matchers.*; import static org.hamcrest.Matchers.*;
import static org.junit.Assert.*; 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; import java.math.BigInteger;
@ -59,7 +61,7 @@ public class QueryMapperUnitTests {
MappingMongoConverter converter = new MappingMongoConverter(factory, context); MappingMongoConverter converter = new MappingMongoConverter(factory, context);
converter.afterPropertiesSet(); converter.afterPropertiesSet();
mapper = new QueryMapper(converter.getConversionService()); mapper = new QueryMapper(converter);
} }
@Test @Test
@ -90,12 +92,12 @@ public class QueryMapperUnitTests {
} }
/** /**
* @see DATADOC-278 * @see DATAMONGO-278
*/ */
@Test @Test
public void translates$NeCorrectly() { 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)); DBObject result = mapper.getMappedObject(criteria.getCriteriaObject(), context.getPersistentEntity(Sample.class));
Object object = result.get("_id"); Object object = result.get("_id");
@ -104,6 +106,18 @@ public class QueryMapperUnitTests {
assertThat(dbObject.get("$ne"), is(ObjectId.class)); 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 { class Sample {
@Id @Id
@ -115,4 +129,8 @@ public class QueryMapperUnitTests {
@Id @Id
private BigInteger id; private BigInteger id;
} }
enum Enum {
INSTANCE;
}
} }

14
spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/query/MongoQueryCreatorUnitTests.java

@ -30,7 +30,6 @@ import org.junit.Before;
import org.junit.Test; import org.junit.Test;
import org.junit.runner.RunWith; import org.junit.runner.RunWith;
import org.mockito.Mock; import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.invocation.InvocationOnMock; import org.mockito.invocation.InvocationOnMock;
import org.mockito.runners.MockitoJUnitRunner; import org.mockito.runners.MockitoJUnitRunner;
import org.mockito.stubbing.Answer; 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.core.support.DefaultRepositoryMetadata;
import org.springframework.data.repository.query.parser.PartTree; import org.springframework.data.repository.query.parser.PartTree;
import com.mongodb.BasicDBObject;
import com.mongodb.DBObject;
/** /**
* Unit test for {@link MongoQueryCreator}. * Unit test for {@link MongoQueryCreator}.
* *
@ -73,13 +69,11 @@ public class MongoQueryCreatorUnitTests {
context = new MongoMappingContext(); context = new MongoMappingContext();
doAnswer(new Answer<Void>() { doAnswer(new Answer<Object>() {
public Void answer(InvocationOnMock invocation) throws Throwable { public Object answer(InvocationOnMock invocation) throws Throwable {
DBObject dbObject = (DBObject) invocation.getArguments()[1]; return invocation.getArguments()[0];
dbObject.put("value", new BasicDBObject("value", "value"));
return null;
} }
}).when(converter).write(any(), Mockito.any(DBObject.class)); }).when(converter).convertToMongoType(any());
} }
@Test @Test

Loading…
Cancel
Save