Browse Source

Tweaked MappingMongoConverter to prefer custom Converters.

Before recursively converting values itself, MappingMongoConverter now checks whether the underlying ConversionService can convert the value to be converted into a Mongo basic type itself.
pull/1/head
Oliver Gierke 15 years ago
parent
commit
4629ebdcfe
  1. 67
      spring-data-mongodb/src/main/java/org/springframework/data/document/mongodb/convert/MappingMongoConverter.java
  2. 106
      spring-data-mongodb/src/test/java/org/springframework/data/document/mongodb/mapping/MappingMongoConverterUnitTests.java

67
spring-data-mongodb/src/main/java/org/springframework/data/document/mongodb/convert/MappingMongoConverter.java

@ -41,6 +41,7 @@ import org.springframework.beans.factory.annotation.Value; @@ -41,6 +41,7 @@ import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.expression.BeanFactoryResolver;
import org.springframework.core.convert.ConversionService;
import org.springframework.core.convert.converter.Converter;
import org.springframework.core.convert.support.ConversionServiceFactory;
import org.springframework.core.convert.support.GenericConversionService;
@ -63,6 +64,7 @@ import org.springframework.expression.spel.support.StandardEvaluationContext; @@ -63,6 +64,7 @@ import org.springframework.expression.spel.support.StandardEvaluationContext;
public class MappingMongoConverter implements MongoConverter, ApplicationContextAware {
private static final String CUSTOM_TYPE_KEY = "_class";
private static final List<Class<?>> MONGO_TYPES = Arrays.asList(Number.class, Date.class, String.class, DBObject.class);
protected static final Log log = LogFactory.getLog(MappingMongoConverter.class);
protected GenericConversionService conversionService = ConversionServiceFactory.createDefaultConversionService();
@ -377,7 +379,14 @@ public class MappingMongoConverter implements MongoConverter, ApplicationContext @@ -377,7 +379,14 @@ public class MappingMongoConverter implements MongoConverter, ApplicationContext
this.applicationContext = applicationContext;
}
/**
* Registers converters for {@link ObjectId} handling, removes plain {@link #toString()} converter and promotes the
* configured {@link ConversionService} to {@link MappingBeanHelper}.
*/
protected void initializeConverters() {
this.conversionService.removeConvertible(Object.class, String.class);
if (!conversionService.canConvert(ObjectId.class, String.class)) {
conversionService.addConverter(ObjectIdToStringConverter.INSTANCE);
conversionService.addConverter(StringToObjectIdConverter.INSTANCE);
@ -386,13 +395,17 @@ public class MappingMongoConverter implements MongoConverter, ApplicationContext @@ -386,13 +395,17 @@ public class MappingMongoConverter implements MongoConverter, ApplicationContext
conversionService.addConverter(ObjectIdToBigIntegerConverter.INSTANCE);
conversionService.addConverter(BigIntegerToIdConverter.INSTANCE);
}
MappingBeanHelper.setConversionService(conversionService);
}
@SuppressWarnings({"unchecked"})
protected void writePropertyInternal(PersistentProperty prop, Object obj, DBObject dbo) {
org.springframework.data.document.mongodb.mapping.DBRef dbref = prop.getField()
.getAnnotation(org.springframework.data.document.mongodb.mapping.DBRef.class);
String name = prop.getName();
if (prop.isCollection()) {
Class<?> type = prop.getType();
BasicDBList dbList = new BasicDBList();
@ -408,20 +421,52 @@ public class MappingMongoConverter implements MongoConverter, ApplicationContext @@ -408,20 +421,52 @@ public class MappingMongoConverter implements MongoConverter, ApplicationContext
}
}
dbo.put(name, dbList);
} else if (null != obj && obj instanceof Map) {
return;
}
if (null != obj && obj instanceof Map) {
BasicDBObject mapDbObj = new BasicDBObject();
writeMapInternal((Map<Object, Object>) obj, mapDbObj);
dbo.put(name, mapDbObj);
} else {
if (null != dbref) {
DBRef dbRef = createDBRef(obj, dbref);
dbo.put(name, dbRef);
} else {
BasicDBObject propDbObj = new BasicDBObject();
write(obj, propDbObj, mappingContext.getPersistentEntity(prop.getTypeInformation()));
dbo.put(name, propDbObj);
return;
}
if (null != dbref) {
DBObject dbRefObj = createDBRef(obj, dbref);
if (null != dbRefObj) {
dbo.put(name, dbRefObj);
return;
}
}
Class<?> basicTargetType = getCustomTargetType(obj);
if (basicTargetType != null) {
dbo.put(name, conversionService.convert(obj, basicTargetType));
return;
}
BasicDBObject propDbObj = new BasicDBObject();
write(obj, propDbObj, mappingContext.getPersistentEntity(prop.getTypeInformation()));
dbo.put(name, propDbObj);
}
/**
* 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;
}
protected void writeMapInternal(Map<Object, Object> obj, DBObject dbo) {
@ -510,7 +555,7 @@ public class MappingMongoConverter implements MongoConverter, ApplicationContext @@ -510,7 +555,7 @@ public class MappingMongoConverter implements MongoConverter, ApplicationContext
continue;
}
if (null != entry.getValue() && entry.getValue() instanceof DBObject) {
m.put(entry.getKey(), read((null != toType ? toType : prop.getTypeInformation().getMapValueType()), (DBObject) entry.getValue()));
m.put(entry.getKey(), read((null != toType ? toType : prop.getMapValueType()), (DBObject) entry.getValue()));
} else {
m.put(entry.getKey(), entry.getValue());
}
@ -518,7 +563,7 @@ public class MappingMongoConverter implements MongoConverter, ApplicationContext @@ -518,7 +563,7 @@ public class MappingMongoConverter implements MongoConverter, ApplicationContext
return m;
} else if (prop.isArray() && dbObj instanceof BasicDBObject && ((DBObject) dbObj).keySet().size() == 0) {
// It's empty
return Array.newInstance(prop.getType().getComponentType(), 0);
return Array.newInstance(prop.getComponentType(), 0);
} else if (prop.isCollection() && dbObj instanceof BasicDBList) {
BasicDBList dbObjList = (BasicDBList) dbObj;
Object[] items = (Object[]) Array.newInstance(prop.getComponentType(), dbObjList.size());

106
spring-data-mongodb/src/test/java/org/springframework/data/document/mongodb/mapping/MappingMongoConverterUnitTests.java

@ -0,0 +1,106 @@ @@ -0,0 +1,106 @@
package org.springframework.data.document.mongodb.mapping;
import static org.hamcrest.Matchers.*;
import static org.junit.Assert.*;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import org.joda.time.LocalDate;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.runners.MockitoJUnitRunner;
import org.springframework.context.ApplicationContext;
import org.springframework.core.convert.converter.Converter;
import org.springframework.data.document.mongodb.convert.MappingMongoConverter;
import org.springframework.data.mapping.BasicMappingContext;
import com.mongodb.BasicDBObject;
import com.mongodb.DBObject;
/**
* Unit tests for {@link MappingMongoConverter}.
*
* @author Oliver Gierke
*/
@RunWith(MockitoJUnitRunner.class)
public class MappingMongoConverterUnitTests {
MappingMongoConverter converter;
BasicMappingContext mappingContext;
@Mock
ApplicationContext applicationContext;
@Before
public void setUp() {
mappingContext = new BasicMappingContext();
mappingContext.setApplicationContext(applicationContext);
converter = new MappingMongoConverter(mappingContext);
}
@Test
public void convertsAddressCorrectly() {
Address address = new Address();
address.city = "New York";
address.street = "Broadway";
DBObject dbObject = new BasicDBObject();
converter.write(address, dbObject);
assertThat(dbObject.get("city").toString(), is("New York"));
assertThat(dbObject.get("street").toString(), is("Broadway"));
}
@Test
public void convertsJodaTimeTypesCorrectly() {
List<Converter<?, ?>> converters = new ArrayList<Converter<?,?>>();
converters.add(new LocalDateToDateConverter());
converters.add(new DateToLocalDateConverter());
List<Class<?>> customSimpleTypes = new ArrayList<Class<?>>();
customSimpleTypes.add(LocalDate.class);
mappingContext.setCustomSimpleTypes(customSimpleTypes);
converter = new MappingMongoConverter(mappingContext, converters);
Person person = new Person();
person.birthDate = new LocalDate();
DBObject dbObject = new BasicDBObject();
converter.write(person, dbObject);
assertTrue(dbObject.get("birthDate") instanceof Date);
Person result = converter.read(Person.class, dbObject);
assertThat(result.birthDate, is(notNullValue()));
}
public static class Address {
String street;
String city;
}
public static class Person {
LocalDate birthDate;
}
private class LocalDateToDateConverter implements Converter<LocalDate, Date> {
public Date convert(LocalDate source) {
return source.toDateMidnight().toDate();
}
}
private class DateToLocalDateConverter implements Converter<Date, LocalDate> {
public LocalDate convert(Date source) {
return new LocalDate(source.getTime());
}
}
}
Loading…
Cancel
Save