Browse Source

DATAMONGO-1210 - Polishing.

Moved getTypeHint(…) method to Field class.

Original pull request: #292.
1.6.x
Oliver Gierke 11 years ago
parent
commit
d6ff5162b3
  1. 89
      spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/MappingMongoConverter.java
  2. 37
      spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/QueryMapper.java
  3. 28
      spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/UpdateMapper.java
  4. 26
      spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/convert/UpdateMapperUnitTests.java

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

@ -135,8 +135,8 @@ public class MappingMongoConverter extends AbstractMongoConverter implements App
* @param typeMapper the typeMapper to set * @param typeMapper the typeMapper to set
*/ */
public void setTypeMapper(MongoTypeMapper typeMapper) { public void setTypeMapper(MongoTypeMapper typeMapper) {
this.typeMapper = typeMapper == null ? new DefaultMongoTypeMapper(DefaultMongoTypeMapper.DEFAULT_TYPE_KEY, this.typeMapper = typeMapper == null
mappingContext) : typeMapper; ? new DefaultMongoTypeMapper(DefaultMongoTypeMapper.DEFAULT_TYPE_KEY, mappingContext) : typeMapper;
} }
/* /*
@ -237,7 +237,8 @@ public class MappingMongoConverter extends AbstractMongoConverter implements App
PersistentEntityParameterValueProvider<MongoPersistentProperty> parameterProvider = new PersistentEntityParameterValueProvider<MongoPersistentProperty>( PersistentEntityParameterValueProvider<MongoPersistentProperty> parameterProvider = new PersistentEntityParameterValueProvider<MongoPersistentProperty>(
entity, provider, path.getCurrentObject()); entity, provider, path.getCurrentObject());
return new ConverterAwareSpELExpressionParameterValueProvider(evaluator, conversionService, parameterProvider, path); return new ConverterAwareSpELExpressionParameterValueProvider(evaluator, conversionService, parameterProvider,
path);
} }
private <S extends Object> S read(final MongoPersistentEntity<S> entity, final DBObject dbo, final ObjectPath path) { private <S extends Object> S read(final MongoPersistentEntity<S> entity, final DBObject dbo, final ObjectPath path) {
@ -505,8 +506,8 @@ public class MappingMongoConverter extends AbstractMongoConverter implements App
: new BasicDBObject(); : new BasicDBObject();
addCustomTypeKeyIfNecessary(type, obj, propDbObj); addCustomTypeKeyIfNecessary(type, obj, propDbObj);
MongoPersistentEntity<?> entity = isSubtype(prop.getType(), obj.getClass()) ? mappingContext MongoPersistentEntity<?> entity = isSubtype(prop.getType(), obj.getClass())
.getPersistentEntity(obj.getClass()) : mappingContext.getPersistentEntity(type); ? mappingContext.getPersistentEntity(obj.getClass()) : mappingContext.getPersistentEntity(type);
writeInternal(obj, propDbObj, entity); writeInternal(obj, propDbObj, entity);
accessor.put(prop, propDbObj); accessor.put(prop, propDbObj);
@ -695,8 +696,10 @@ public class MappingMongoConverter extends AbstractMongoConverter implements App
} }
if (mapKeyDotReplacement == null) { if (mapKeyDotReplacement == null) {
throw new MappingException(String.format("Map key %s contains dots but no replacement was configured! Make " throw new MappingException(String.format(
+ "sure map keys don't contain dots in the first place or configure an appropriate replacement!", source)); "Map key %s contains dots but no replacement was configured! Make "
+ "sure map keys don't contain dots in the first place or configure an appropriate replacement!",
source));
} }
return source.replaceAll("\\.", mapKeyDotReplacement); return source.replaceAll("\\.", mapKeyDotReplacement);
@ -714,8 +717,8 @@ public class MappingMongoConverter extends AbstractMongoConverter implements App
return (String) key; return (String) key;
} }
return conversions.hasCustomWriteTarget(key.getClass(), String.class) ? (String) getPotentiallyConvertedSimpleWrite(key) return conversions.hasCustomWriteTarget(key.getClass(), String.class)
: key.toString(); ? (String) getPotentiallyConvertedSimpleWrite(key) : key.toString();
} }
/** /**
@ -884,16 +887,16 @@ public class MappingMongoConverter extends AbstractMongoConverter implements App
Class<?> rawComponentType = componentType == null ? null : componentType.getType(); Class<?> rawComponentType = componentType == null ? null : componentType.getType();
collectionType = Collection.class.isAssignableFrom(collectionType) ? collectionType : List.class; collectionType = Collection.class.isAssignableFrom(collectionType) ? collectionType : List.class;
Collection<Object> items = targetType.getType().isArray() ? new ArrayList<Object>() : CollectionFactory Collection<Object> items = targetType.getType().isArray() ? new ArrayList<Object>()
.createCollection(collectionType, rawComponentType, sourceValue.size()); : CollectionFactory.createCollection(collectionType, rawComponentType, sourceValue.size());
for (int i = 0; i < sourceValue.size(); i++) { for (int i = 0; i < sourceValue.size(); i++) {
Object dbObjItem = sourceValue.get(i); Object dbObjItem = sourceValue.get(i);
if (dbObjItem instanceof DBRef) { if (dbObjItem instanceof DBRef) {
items.add(DBRef.class.equals(rawComponentType) ? dbObjItem : read(componentType, readRef((DBRef) dbObjItem), items.add(
path)); DBRef.class.equals(rawComponentType) ? dbObjItem : read(componentType, readRef((DBRef) dbObjItem), path));
} else if (dbObjItem instanceof DBObject) { } else if (dbObjItem instanceof DBObject) {
items.add(read(componentType, (DBObject) dbObjItem, path)); items.add(read(componentType, (DBObject) dbObjItem, path));
} else { } else {
@ -1011,14 +1014,14 @@ public class MappingMongoConverter extends AbstractMongoConverter implements App
this.write(obj, newDbo); this.write(obj, newDbo);
if (typeInformation == null) { if (typeInformation == null) {
return removeTypeInfoRecursively(newDbo); return removeTypeInfo(newDbo, true);
} }
if (typeInformation.getType().equals(NestedDocument.class)) { if (typeInformation.getType().equals(NestedDocument.class)) {
return removeTypeInfo(newDbo); return removeTypeInfo(newDbo, false);
} }
return !obj.getClass().equals(typeInformation.getType()) ? newDbo : removeTypeInfoRecursively(newDbo); return !obj.getClass().equals(typeInformation.getType()) ? newDbo : removeTypeInfo(newDbo, true);
} }
public BasicDBList maybeConvertList(Iterable<?> source, TypeInformation<?> typeInformation) { public BasicDBList maybeConvertList(Iterable<?> source, TypeInformation<?> typeInformation) {
@ -1032,12 +1035,13 @@ public class MappingMongoConverter extends AbstractMongoConverter implements App
} }
/** /**
* Removes only the type information from the root document. * Removes the type information from the entire conversion result.
* *
* @param object * @param object
* @param recursively whether to apply the removal recursively
* @return * @return
*/ */
private Object removeTypeInfo(Object object) { private Object removeTypeInfo(Object object, boolean recursively) {
if (!(object instanceof DBObject)) { if (!(object instanceof DBObject)) {
return object; return object;
@ -1045,47 +1049,29 @@ public class MappingMongoConverter extends AbstractMongoConverter implements App
DBObject dbObject = (DBObject) object; DBObject dbObject = (DBObject) object;
String keyToRemove = null; String keyToRemove = null;
for (String key : dbObject.keySet()) { for (String key : dbObject.keySet()) {
if (typeMapper.isTypeKey(key)) { if (recursively) {
keyToRemove = key;
}
}
if (keyToRemove != null) { Object value = dbObject.get(key);
dbObject.removeField(keyToRemove);
}
return dbObject; if (value instanceof BasicDBList) {
for (Object element : (BasicDBList) value) {
removeTypeInfo(element, recursively);
}
} else {
removeTypeInfo(value, recursively);
} }
/**
* Removes the type information from the entire 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)) { if (typeMapper.isTypeKey(key)) {
keyToRemove = key; keyToRemove = key;
}
Object value = dbObject.get(key); if (!recursively) {
if (value instanceof BasicDBList) { break;
for (Object element : (BasicDBList) value) {
removeTypeInfoRecursively(element);
} }
} else {
removeTypeInfoRecursively(value);
} }
} }
@ -1149,8 +1135,8 @@ public class MappingMongoConverter extends AbstractMongoConverter implements App
* *
* @author Oliver Gierke * @author Oliver Gierke
*/ */
private class ConverterAwareSpELExpressionParameterValueProvider extends private class ConverterAwareSpELExpressionParameterValueProvider
SpELExpressionParameterValueProvider<MongoPersistentProperty> { extends SpELExpressionParameterValueProvider<MongoPersistentProperty> {
private final ObjectPath path; private final ObjectPath path;
@ -1162,7 +1148,8 @@ public class MappingMongoConverter extends AbstractMongoConverter implements App
* @param delegate must not be {@literal null}. * @param delegate must not be {@literal null}.
*/ */
public ConverterAwareSpELExpressionParameterValueProvider(SpELExpressionEvaluator evaluator, public ConverterAwareSpELExpressionParameterValueProvider(SpELExpressionEvaluator evaluator,
ConversionService conversionService, ParameterValueProvider<MongoPersistentProperty> delegate, ObjectPath path) { ConversionService conversionService, ParameterValueProvider<MongoPersistentProperty> delegate,
ObjectPath path) {
super(evaluator, conversionService, delegate); super(evaluator, conversionService, delegate);
this.path = path; this.path = path;

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

@ -34,10 +34,13 @@ import org.springframework.data.mapping.PropertyReferenceException;
import org.springframework.data.mapping.context.MappingContext; import org.springframework.data.mapping.context.MappingContext;
import org.springframework.data.mapping.context.PersistentPropertyPath; import org.springframework.data.mapping.context.PersistentPropertyPath;
import org.springframework.data.mapping.model.MappingException; import org.springframework.data.mapping.model.MappingException;
import org.springframework.data.mongodb.core.convert.MappingMongoConverter.NestedDocument;
import org.springframework.data.mongodb.core.mapping.MongoPersistentEntity; import org.springframework.data.mongodb.core.mapping.MongoPersistentEntity;
import org.springframework.data.mongodb.core.mapping.MongoPersistentProperty; import org.springframework.data.mongodb.core.mapping.MongoPersistentProperty;
import org.springframework.data.mongodb.core.mapping.MongoPersistentProperty.PropertyToFieldNameConverter; import org.springframework.data.mongodb.core.mapping.MongoPersistentProperty.PropertyToFieldNameConverter;
import org.springframework.data.mongodb.core.query.Query; import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.util.ClassTypeInformation;
import org.springframework.data.util.TypeInformation;
import org.springframework.util.Assert; import org.springframework.util.Assert;
import com.mongodb.BasicDBList; import com.mongodb.BasicDBList;
@ -58,6 +61,7 @@ public class QueryMapper {
private static final List<String> DEFAULT_ID_NAMES = Arrays.asList("id", "_id"); private static final List<String> DEFAULT_ID_NAMES = Arrays.asList("id", "_id");
private static final DBObject META_TEXT_SCORE = new BasicDBObject("$meta", "textScore"); private static final DBObject META_TEXT_SCORE = new BasicDBObject("$meta", "textScore");
static final ClassTypeInformation<?> NESTED_DOCUMENT = ClassTypeInformation.from(NestedDocument.class);
private enum MetaMapping { private enum MetaMapping {
FORCE, WHEN_PRESENT, IGNORE; FORCE, WHEN_PRESENT, IGNORE;
@ -250,8 +254,8 @@ public class QueryMapper {
boolean needsAssociationConversion = property.isAssociation() && !keyword.isExists(); boolean needsAssociationConversion = property.isAssociation() && !keyword.isExists();
Object value = keyword.getValue(); Object value = keyword.getValue();
Object convertedValue = needsAssociationConversion ? convertAssociation(value, property) : getMappedValue( Object convertedValue = needsAssociationConversion ? convertAssociation(value, property)
property.with(keyword.getKey()), value); : getMappedValue(property.with(keyword.getKey()), value);
return new BasicDBObject(keyword.key, convertedValue); return new BasicDBObject(keyword.key, convertedValue);
} }
@ -473,8 +477,8 @@ public class QueryMapper {
} }
try { try {
return conversionService.canConvert(id.getClass(), ObjectId.class) ? conversionService return conversionService.canConvert(id.getClass(), ObjectId.class) ? conversionService.convert(id, ObjectId.class)
.convert(id, ObjectId.class) : delegateConvertToMongoType(id, null); : delegateConvertToMongoType(id, null);
} catch (ConversionException o_O) { } catch (ConversionException o_O) {
return delegateConvertToMongoType(id, null); return delegateConvertToMongoType(id, null);
} }
@ -657,6 +661,10 @@ public class QueryMapper {
public Association<MongoPersistentProperty> getAssociation() { public Association<MongoPersistentProperty> getAssociation() {
return null; return null;
} }
public TypeInformation<?> getTypeHint() {
return ClassTypeInformation.OBJECT;
}
} }
/** /**
@ -862,6 +870,27 @@ public class QueryMapper {
protected Converter<MongoPersistentProperty, String> getAssociationConverter() { protected Converter<MongoPersistentProperty, String> getAssociationConverter() {
return new AssociationConverter(getAssociation()); return new AssociationConverter(getAssociation());
} }
/*
* (non-Javadoc)
* @see org.springframework.data.mongodb.core.convert.QueryMapper.Field#getTypeHint()
*/
@Override
public TypeInformation<?> getTypeHint() {
MongoPersistentProperty property = getProperty();
if (property == null) {
return super.getTypeHint();
}
if (property.getActualType().isInterface()
|| java.lang.reflect.Modifier.isAbstract(property.getActualType().getModifiers())) {
return ClassTypeInformation.OBJECT;
}
return NESTED_DOCUMENT;
}
} }
/** /**

28
spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/UpdateMapper.java

@ -22,7 +22,6 @@ import java.util.Map.Entry;
import org.springframework.core.convert.converter.Converter; import org.springframework.core.convert.converter.Converter;
import org.springframework.data.mapping.Association; import org.springframework.data.mapping.Association;
import org.springframework.data.mapping.context.MappingContext; import org.springframework.data.mapping.context.MappingContext;
import org.springframework.data.mongodb.core.convert.MappingMongoConverter.NestedDocument;
import org.springframework.data.mongodb.core.mapping.MongoPersistentEntity; import org.springframework.data.mongodb.core.mapping.MongoPersistentEntity;
import org.springframework.data.mongodb.core.mapping.MongoPersistentProperty; import org.springframework.data.mongodb.core.mapping.MongoPersistentProperty;
import org.springframework.data.mongodb.core.mapping.MongoPersistentProperty.PropertyToFieldNameConverter; import org.springframework.data.mongodb.core.mapping.MongoPersistentProperty.PropertyToFieldNameConverter;
@ -45,7 +44,6 @@ import com.mongodb.DBObject;
*/ */
public class UpdateMapper extends QueryMapper { public class UpdateMapper extends QueryMapper {
private static final ClassTypeInformation<?> NESTED_DOCUMENT = ClassTypeInformation.from(NestedDocument.class);
private final MongoConverter converter; private final MongoConverter converter;
/** /**
@ -68,8 +66,8 @@ public class UpdateMapper extends QueryMapper {
*/ */
@Override @Override
protected Object delegateConvertToMongoType(Object source, MongoPersistentEntity<?> entity) { protected Object delegateConvertToMongoType(Object source, MongoPersistentEntity<?> entity) {
return entity == null ? super.delegateConvertToMongoType(source, null) : converter.convertToMongoType(source, return entity == null ? super.delegateConvertToMongoType(source, null)
getTypeHintForEntity(entity)); : converter.convertToMongoType(source, getTypeHintForEntity(entity));
} }
/* /*
@ -137,22 +135,10 @@ public class UpdateMapper extends QueryMapper {
private DBObject getMappedValue(Field field, Modifier modifier) { private DBObject getMappedValue(Field field, Modifier modifier) {
Object value = converter.convertToMongoType(modifier.getValue(), getTypeHintForField(field)); TypeInformation<?> typeHint = field == null ? ClassTypeInformation.OBJECT : field.getTypeHint();
return new BasicDBObject(modifier.getKey(), value);
}
private TypeInformation<?> getTypeHintForField(Field field) {
if (field == null || field.getProperty() == null) {
return ClassTypeInformation.OBJECT;
}
if (field.getProperty().getActualType().isInterface() Object value = converter.convertToMongoType(modifier.getValue(), typeHint);
|| java.lang.reflect.Modifier.isAbstract(field.getProperty().getActualType().getModifiers())) { return new BasicDBObject(modifier.getKey(), value);
return ClassTypeInformation.OBJECT;
}
return NESTED_DOCUMENT;
} }
private TypeInformation<?> getTypeHintForEntity(MongoPersistentEntity<?> entity) { private TypeInformation<?> getTypeHintForEntity(MongoPersistentEntity<?> entity) {
@ -177,8 +163,8 @@ public class UpdateMapper extends QueryMapper {
protected Field createPropertyField(MongoPersistentEntity<?> entity, String key, protected Field createPropertyField(MongoPersistentEntity<?> entity, String key,
MappingContext<? extends MongoPersistentEntity<?>, MongoPersistentProperty> mappingContext) { MappingContext<? extends MongoPersistentEntity<?>, MongoPersistentProperty> mappingContext) {
return entity == null ? super.createPropertyField(entity, key, mappingContext) : // return entity == null ? super.createPropertyField(entity, key, mappingContext)
new MetadataBackedUpdateField(entity, key, mappingContext); : new MetadataBackedUpdateField(entity, key, mappingContext);
} }
/** /**

26
spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/convert/UpdateMapperUnitTests.java

@ -360,8 +360,8 @@ public class UpdateMapperUnitTests {
public void rendersNestedDbRefCorrectly() { public void rendersNestedDbRefCorrectly() {
Update update = new Update().pull("nested.dbRefAnnotatedList.id", "2"); Update update = new Update().pull("nested.dbRefAnnotatedList.id", "2");
DBObject mappedObject = mapper DBObject mappedObject = mapper.getMappedObject(update.getUpdateObject(),
.getMappedObject(update.getUpdateObject(), context.getPersistentEntity(Wrapper.class)); context.getPersistentEntity(Wrapper.class));
DBObject pullClause = getAsDBObject(mappedObject, "$pull"); DBObject pullClause = getAsDBObject(mappedObject, "$pull");
assertThat(pullClause.containsField("mapped.dbRefAnnotatedList"), is(true)); assertThat(pullClause.containsField("mapped.dbRefAnnotatedList"), is(true));
@ -590,8 +590,7 @@ public class UpdateMapperUnitTests {
assertThat(mappedUpdate, assertThat(mappedUpdate,
isBsonObject().notContaining("$addToSet.listHoldingConcretyTypeWithInterfaceTypeAttribute.$each.[0]._class")); isBsonObject().notContaining("$addToSet.listHoldingConcretyTypeWithInterfaceTypeAttribute.$each.[0]._class"));
assertThat( assertThat(mappedUpdate,
mappedUpdate,
isBsonObject().containing( isBsonObject().containing(
"$addToSet.listHoldingConcretyTypeWithInterfaceTypeAttribute.$each.[0].interfaceType._class", "$addToSet.listHoldingConcretyTypeWithInterfaceTypeAttribute.$each.[0].interfaceType._class",
ModelImpl.class.getName())); ModelImpl.class.getName()));
@ -611,10 +610,8 @@ public class UpdateMapperUnitTests {
context.getPersistentEntity(DomainTypeWrappingConcreteyTypeHavingListOfInterfaceTypeAttributes.class)); context.getPersistentEntity(DomainTypeWrappingConcreteyTypeHavingListOfInterfaceTypeAttributes.class));
assertThat(mappedUpdate, isBsonObject().notContaining("$set.concreteTypeWithListAttributeOfInterfaceType._class")); assertThat(mappedUpdate, isBsonObject().notContaining("$set.concreteTypeWithListAttributeOfInterfaceType._class"));
assertThat( assertThat(mappedUpdate, isBsonObject()
mappedUpdate, .containing("$set.concreteTypeWithListAttributeOfInterfaceType.models.[0]._class", ModelImpl.class.getName()));
isBsonObject().containing("$set.concreteTypeWithListAttributeOfInterfaceType.models.[0]._class",
ModelImpl.class.getName()));
} }
static class DomainTypeWrappingConcreteyTypeHavingListOfInterfaceTypeAttributes { static class DomainTypeWrappingConcreteyTypeHavingListOfInterfaceTypeAttributes {
@ -665,7 +662,7 @@ public class UpdateMapperUnitTests {
private @Id String id; private @Id String id;
@org.springframework.data.mongodb.core.mapping.DBRef// @org.springframework.data.mongodb.core.mapping.DBRef //
private InterfaceDocumentDefinitionWithoutId referencedDocument; private InterfaceDocumentDefinitionWithoutId referencedDocument;
public String getId() { public String getId() {
@ -726,10 +723,10 @@ public class UpdateMapperUnitTests {
String id; String id;
@Field("aliased")// @Field("aliased") //
List<? extends AbstractChildClass> list; List<? extends AbstractChildClass> list;
@Field// @Field //
List<Model> listOfInterface; List<Model> listOfInterface;
public ParentClass(String id, List<? extends AbstractChildClass> list) { public ParentClass(String id, List<? extends AbstractChildClass> list) {
@ -791,10 +788,10 @@ public class UpdateMapperUnitTests {
@Id public String id; @Id public String id;
@org.springframework.data.mongodb.core.mapping.DBRef// @org.springframework.data.mongodb.core.mapping.DBRef //
public List<Entity> dbRefAnnotatedList; public List<Entity> dbRefAnnotatedList;
@org.springframework.data.mongodb.core.mapping.DBRef// @org.springframework.data.mongodb.core.mapping.DBRef //
public Entity dbRefProperty; public Entity dbRefProperty;
} }
@ -814,13 +811,12 @@ public class UpdateMapperUnitTests {
} }
static class NestedDocument { static class NestedDocument {
String name; String name;
public NestedDocument(String name) { public NestedDocument(String name) {
super(); super();
this.name = name; this.name = name;
} }
} }
} }

Loading…
Cancel
Save