diff --git a/spring-data-mongodb/pom.xml b/spring-data-mongodb/pom.xml
index 1c579cb49..a2213d637 100644
--- a/spring-data-mongodb/pom.xml
+++ b/spring-data-mongodb/pom.xml
@@ -41,6 +41,7 @@
org.springframework.data
spring-data-commons-core
+ ${data.commons.version}
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/document/mongodb/convert/MappingMongoConverter.java b/spring-data-mongodb/src/main/java/org/springframework/data/document/mongodb/convert/MappingMongoConverter.java
index 8cf6a731f..016a61b37 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/document/mongodb/convert/MappingMongoConverter.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/document/mongodb/convert/MappingMongoConverter.java
@@ -51,6 +51,7 @@ import java.util.*;
@SuppressWarnings({"unchecked"})
public class MappingMongoConverter implements MongoConverter, ApplicationContextAware {
+ private static final String CUSTOM_TYPE_KEY = "_class";
protected static final Log log = LogFactory.getLog(MappingMongoConverter.class);
protected GenericConversionService conversionService = ConversionServiceFactory.createDefaultConversionService();
@@ -116,15 +117,6 @@ public class MappingMongoConverter implements MongoConverter, ApplicationContext
return null;
}
- final StandardEvaluationContext spelCtx = new StandardEvaluationContext();
- if (null != applicationContext) {
- spelCtx.setBeanResolver(new BeanFactoryResolver(applicationContext));
- }
- String[] keySet = dbo.keySet().toArray(new String[]{});
- for (String key : keySet) {
- spelCtx.setVariable(key, dbo.get(key));
- }
-
if ((clazz.isArray()
|| (clazz.isAssignableFrom(Collection.class)
|| clazz.isAssignableFrom(List.class)))
@@ -147,7 +139,24 @@ public class MappingMongoConverter implements MongoConverter, ApplicationContext
}
// Retrieve persistent entity info
- PersistentEntity entity = mappingContext.getPersistentEntity(clazz);
+ PersistentEntity persistentEntity = mappingContext.getPersistentEntity(clazz);
+ if (persistentEntity == null) {
+ persistentEntity = mappingContext.addPersistentEntity(clazz);
+ }
+
+ return read(persistentEntity, dbo);
+ }
+
+ private S read(PersistentEntity entity, final DBObject dbo) {
+
+ final StandardEvaluationContext spelCtx = new StandardEvaluationContext();
+ if (null != applicationContext) {
+ spelCtx.setBeanResolver(new BeanFactoryResolver(applicationContext));
+ }
+ String[] keySet = dbo.keySet().toArray(new String[] {});
+ for (String key : keySet) {
+ spelCtx.setVariable(key, dbo.get(key));
+ }
final List ctorParamNames = new ArrayList();
final S instance = MappingBeanHelper.constructInstance(entity, new PreferredConstructor.ParameterValueProvider() {
@@ -174,7 +183,7 @@ public class MappingMongoConverter implements MongoConverter, ApplicationContext
}, spelCtx);
// Set the ID
- final PersistentProperty> idProperty = entity.getIdProperty();
+ PersistentProperty idProperty = entity.getIdProperty();
if (dbo.containsField("_id") || null != idProperty) {
Object idObj = dbo.get("_id");
try {
@@ -188,14 +197,18 @@ public class MappingMongoConverter implements MongoConverter, ApplicationContext
// Set properties not already set in the constructor
entity.doWithProperties(new PropertyHandler() {
- public void doWithPersistentProperty(PersistentProperty> prop) {
- String name = prop.getName();
- if (null != idProperty && name.equals(idProperty.getName())) {
- return;
- }
- if (prop.isAssociation()) {
- return;
+ public void doWithPersistentProperty(PersistentProperty prop) {
+ if (!ctorParamNames.contains(prop.getName())) {
+ Object obj = getValueInternal(prop, dbo, spelCtx, prop.getValueAnnotation());
+ try {
+ MappingBeanHelper.setProperty(instance, prop, obj, useFieldAccessOnly);
+ } catch (IllegalAccessException e) {
+ throw new MappingException(e.getMessage(), e);
+ } catch (InvocationTargetException e) {
+ throw new MappingException(e.getMessage(), e);
+ }
}
+
if (ctorParamNames.contains(prop.getName())) {
return;
}
@@ -214,7 +227,7 @@ public class MappingMongoConverter implements MongoConverter, ApplicationContext
// Handle associations
entity.doWithAssociations(new AssociationHandler() {
public void doWithAssociation(Association association) {
- PersistentProperty> inverseProp = association.getInverse();
+ PersistentProperty inverseProp = association.getInverse();
Object obj = getValueInternal(inverseProp, dbo, spelCtx, inverseProp.getValueAnnotation());
try {
MappingBeanHelper.setProperty(instance, inverseProp, obj);
@@ -246,6 +259,15 @@ public class MappingMongoConverter implements MongoConverter, ApplicationContext
}
PersistentEntity> entity = mappingContext.getPersistentEntity(obj.getClass());
+ write(obj, dbo, entity);
+ }
+
+ protected void write(final Object obj, final DBObject dbo, PersistentEntity> entity) {
+
+ if (obj == null) {
+ return;
+ }
+
if (null == entity) {
// Must not have explictly added this entity yet
entity = mappingContext.addPersistentEntity(obj.getClass());
@@ -256,7 +278,7 @@ public class MappingMongoConverter implements MongoConverter, ApplicationContext
}
// Write the ID
- final PersistentProperty> idProperty = entity.getIdProperty();
+ final PersistentProperty idProperty = entity.getIdProperty();
if (!dbo.containsField("_id") && null != idProperty) {
Object idObj = null;
try {
@@ -274,7 +296,7 @@ public class MappingMongoConverter implements MongoConverter, ApplicationContext
// Write the properties
entity.doWithProperties(new PropertyHandler() {
- public void doWithPersistentProperty(PersistentProperty> prop) {
+ public void doWithPersistentProperty(PersistentProperty prop) {
String name = prop.getName();
if (null != idProperty && name.equals(idProperty.getName())) {
return;
@@ -304,7 +326,7 @@ public class MappingMongoConverter implements MongoConverter, ApplicationContext
entity.doWithAssociations(new AssociationHandler() {
public void doWithAssociation(Association association) {
- PersistentProperty> inverseProp = association.getInverse();
+ PersistentProperty inverseProp = association.getInverse();
Class> type = inverseProp.getType();
Object propertyObj = null;
try {
@@ -336,7 +358,7 @@ public class MappingMongoConverter implements MongoConverter, ApplicationContext
}
}
- protected void writePropertyInternal(PersistentProperty> prop, Object obj, DBObject dbo) {
+ 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();
@@ -352,8 +374,7 @@ public class MappingMongoConverter implements MongoConverter, ApplicationContext
}
} else {
BasicDBObject propDbObj = new BasicDBObject();
- //dbo.put("_class", prop.getType().getName());
- write(propObjItem, propDbObj);
+ write(propObjItem, propDbObj, mappingContext.getPersistentEntity(prop.getTypeInformation()));
dbList.add(propDbObj);
}
}
@@ -370,7 +391,7 @@ public class MappingMongoConverter implements MongoConverter, ApplicationContext
}
} else {
BasicDBObject propDbObj = new BasicDBObject();
- write(obj, propDbObj);
+ write(obj, propDbObj, mappingContext.getPersistentEntity(prop.getTypeInformation()));
dbo.put(name, propDbObj);
}
}
@@ -411,7 +432,7 @@ public class MappingMongoConverter implements MongoConverter, ApplicationContext
}
DBObject dbo = new BasicDBObject();
- PersistentProperty> idProperty = targetEntity.getIdProperty();
+ PersistentProperty idProperty = targetEntity.getIdProperty();
ObjectId id = null;
try {
id = MappingBeanHelper.getProperty(target, idProperty, ObjectId.class, useFieldAccessOnly);
@@ -436,7 +457,7 @@ public class MappingMongoConverter implements MongoConverter, ApplicationContext
return dbo;
}
- protected Object getValueInternal(PersistentProperty> prop, DBObject dbo, StandardEvaluationContext ctx, Value spelExpr) {
+ protected Object getValueInternal(PersistentProperty prop, DBObject dbo, StandardEvaluationContext ctx, Value spelExpr) {
String name = prop.getName();
Object o;
if (null != spelExpr) {
@@ -449,27 +470,26 @@ public class MappingMongoConverter implements MongoConverter, ApplicationContext
}
Object dbObj = from.get(name);
if (dbObj instanceof DBObject) {
- Class> type = prop.getType();
- if (type.isAssignableFrom(Map.class) && dbObj instanceof DBObject) {
- Map m = new LinkedHashMap();
+ if (prop.isMap() && dbObj instanceof DBObject) {
+
+ // We have to find a potentially stored class to be used first.
+ Class> toType = findTypeToBeUsed((DBObject) dbObj);
+ Map m = new LinkedHashMap();
+
for (Map.Entry entry : ((Map) ((DBObject) dbObj).toMap()).entrySet()) {
- Class> toType = null;
- if (entry.getKey().equals("_class")) {
- try {
- toType = Class.forName(entry.getValue().toString());
- } catch (ClassNotFoundException e) {
- throw new MappingException(e.getMessage(), e);
- }
- } else if (null != entry.getValue() && entry.getValue() instanceof DBObject) {
- m.put(entry.getKey(), read((null != toType ? toType : type), (DBObject) entry.getValue()));
+ if (entry.getKey().equals(CUSTOM_TYPE_KEY)) {
+ continue;
+ }
+ if (null != entry.getValue() && entry.getValue() instanceof DBObject) {
+ m.put(entry.getKey(), read((null != toType ? toType : prop.getTypeInformation().getMapValueType()), (DBObject) entry.getValue()));
} else {
m.put(entry.getKey(), entry.getValue());
}
}
return m;
- } else if (type.isArray() && dbObj instanceof BasicDBObject && ((DBObject) dbObj).keySet().size() == 0) {
+ } else if (prop.isArray() && dbObj instanceof BasicDBObject && ((DBObject) dbObj).keySet().size() == 0) {
// It's empty
- return Array.newInstance(type.getComponentType(), 0);
+ return Array.newInstance(prop.getType().getComponentType(), 0);
} else if (prop.isCollection() && dbObj instanceof BasicDBList) {
BasicDBList dbObjList = (BasicDBList) dbObj;
Object[] items = (Object[]) Array.newInstance(prop.getComponentType(), dbObjList.size());
@@ -485,6 +505,7 @@ public class MappingMongoConverter implements MongoConverter, ApplicationContext
}
return Arrays.asList(items);
}
+
// It's a complex object, have to read it in
if (dbo.containsField("_class")) {
try {
@@ -495,7 +516,7 @@ public class MappingMongoConverter implements MongoConverter, ApplicationContext
throw new MappingException(e.getMessage(), e);
}
} else {
- o = read(type, (DBObject) dbObj);
+ o = read(mappingContext.getPersistentEntity(prop.getTypeInformation()), (DBObject) dbObj);
}
} else {
o = dbObj;
@@ -503,6 +524,22 @@ public class MappingMongoConverter implements MongoConverter, ApplicationContext
}
return o;
}
+
+ /**
+ * Returns the type to be used to convert the DBObject given to.
+ *
+ * @param dbObject
+ * @return
+ */
+ private Class> findTypeToBeUsed(DBObject dbObject) {
+ Object classToBeUsed = dbObject.get(CUSTOM_TYPE_KEY);
+
+ try {
+ return Class.forName(classToBeUsed.toString());
+ } catch (ClassNotFoundException e) {
+ throw new MappingException(e.getMessage(), e);
+ }
+ }
/**
* Simple singleton to convert {@link ObjectId}s to their {@link String} representation.
@@ -556,16 +593,16 @@ public class MappingMongoConverter implements MongoConverter, ApplicationContext
}
}
- protected class PersistentPropertyWrapper {
- private final PersistentProperty property;
+ protected class PersistentPropertyWrapper {
+ private final PersistentProperty property;
private final DBObject target;
- public PersistentPropertyWrapper(PersistentProperty property, DBObject target) {
+ public PersistentPropertyWrapper(PersistentProperty property, DBObject target) {
this.property = property;
this.target = target;
}
- public PersistentProperty getProperty() {
+ public PersistentProperty getProperty() {
return property;
}
@@ -573,5 +610,4 @@ public class MappingMongoConverter implements MongoConverter, ApplicationContext
return target;
}
}
-
}
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/document/mongodb/mapping/MongoMappingConfigurationBuilder.java b/spring-data-mongodb/src/main/java/org/springframework/data/document/mongodb/mapping/MongoMappingConfigurationBuilder.java
index 2d8e4891b..e1689008c 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/document/mongodb/mapping/MongoMappingConfigurationBuilder.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/document/mongodb/mapping/MongoMappingConfigurationBuilder.java
@@ -33,6 +33,7 @@ import org.springframework.data.document.mongodb.index.Indexed;
import org.springframework.data.mapping.BasicMappingConfigurationBuilder;
import org.springframework.data.mapping.MappingBeanHelper;
import org.springframework.data.mapping.model.*;
+import org.springframework.data.util.TypeInformation;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Field;
@@ -59,8 +60,8 @@ public class MongoMappingConfigurationBuilder extends BasicMappingConfigurationB
}
@Override
- public PersistentProperty createPersistentProperty(Field field, PropertyDescriptor descriptor, Class type) throws MappingConfigurationException {
- PersistentProperty property = new MongoPersistentProperty(field.getName(), type, field, descriptor);
+ public PersistentProperty createPersistentProperty(Field field, PropertyDescriptor descriptor, TypeInformation information) throws MappingConfigurationException {
+ PersistentProperty property = new MongoPersistentProperty(field, descriptor, information);
if (field.isAnnotationPresent(Indexed.class)) {
Indexed index = field.getAnnotation(Indexed.class);
String collection = index.collection();
@@ -75,10 +76,11 @@ public class MongoMappingConfigurationBuilder extends BasicMappingConfigurationB
return property;
}
- @SuppressWarnings({"unchecked"})
@Override
- public PersistentEntity createPersistentEntity(Class type, MappingContext mappingContext) throws MappingConfigurationException {
- MongoPersistentEntity entity = new MongoPersistentEntity(mappingContext, type);
+ public PersistentEntity createPersistentEntity(TypeInformation typeInformation, MappingContext mappingContext) throws MappingConfigurationException {
+
+ MongoPersistentEntity entity = new MongoPersistentEntity(mappingContext, typeInformation);
+ Class> type = typeInformation.getType();
// Check for special collection setting
if (type.isAnnotationPresent(Document.class)) {
@@ -117,7 +119,7 @@ public class MongoMappingConfigurationBuilder extends BasicMappingConfigurationB
}
@Override
- public Association createAssociation(PersistentProperty> property) {
+ public Association createAssociation(PersistentProperty property) {
return super.createAssociation(property);
}
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/document/mongodb/mapping/MongoPersistentEntity.java b/spring-data-mongodb/src/main/java/org/springframework/data/document/mongodb/mapping/MongoPersistentEntity.java
index e3d85626c..dee231fd8 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/document/mongodb/mapping/MongoPersistentEntity.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/document/mongodb/mapping/MongoPersistentEntity.java
@@ -18,17 +18,18 @@ package org.springframework.data.document.mongodb.mapping;
import org.springframework.data.mapping.BasicPersistentEntity;
import org.springframework.data.mapping.model.MappingContext;
+import org.springframework.data.util.TypeInformation;
/**
* @author Jon Brisbin
*/
-public class MongoPersistentEntity extends BasicPersistentEntity {
+public class MongoPersistentEntity extends BasicPersistentEntity {
protected String collection;
- @SuppressWarnings({"unchecked"})
- public MongoPersistentEntity(MappingContext mappingContext, Class type) {
- super(mappingContext, type);
+
+ public MongoPersistentEntity(MappingContext mappingContext, TypeInformation typeInformation) {
+ super(mappingContext, typeInformation);
}
public String getCollection() {
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/document/mongodb/mapping/MongoPersistentProperty.java b/spring-data-mongodb/src/main/java/org/springframework/data/document/mongodb/mapping/MongoPersistentProperty.java
index 02e640b6a..4d89918bd 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/document/mongodb/mapping/MongoPersistentProperty.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/document/mongodb/mapping/MongoPersistentProperty.java
@@ -23,6 +23,7 @@ import java.util.Set;
import org.bson.types.ObjectId;
import org.springframework.data.mapping.BasicPersistentProperty;
+import org.springframework.data.util.TypeInformation;
/**
* Mongo specific
@@ -31,7 +32,7 @@ import org.springframework.data.mapping.BasicPersistentProperty;
*
* @author Oliver Gierke
*/
-public class MongoPersistentProperty extends BasicPersistentProperty {
+public class MongoPersistentProperty extends BasicPersistentProperty {
private static final Set> SUPPORTED_ID_TYPES = new HashSet>();
private static final Set SUPPORTED_ID_PROPERTY_NAMES = new HashSet();
@@ -48,14 +49,13 @@ public class MongoPersistentProperty extends BasicPersistentProperty {
/**
* Creates a new {@link MongoPersistentProperty}.
*
- * @param name
- * @param type
* @param field
* @param propertyDescriptor
+ * @param owningTypeInformation
*/
- public MongoPersistentProperty(String name, Class type, Field field,
- PropertyDescriptor propertyDescriptor) {
- super(name, type, field, propertyDescriptor);
+ public MongoPersistentProperty(Field field,
+ PropertyDescriptor propertyDescriptor, TypeInformation owningTypeInformation) {
+ super(field, propertyDescriptor, owningTypeInformation);
}
/**
diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/document/mongodb/mapping/GenericMappingTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/document/mongodb/mapping/GenericMappingTests.java
new file mode 100644
index 000000000..a74a81e37
--- /dev/null
+++ b/spring-data-mongodb/src/test/java/org/springframework/data/document/mongodb/mapping/GenericMappingTests.java
@@ -0,0 +1,76 @@
+package org.springframework.data.document.mongodb.mapping;
+
+import static org.hamcrest.CoreMatchers.*;
+import static org.junit.Assert.*;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.springframework.data.document.mongodb.convert.MappingMongoConverter;
+import org.springframework.data.document.mongodb.convert.MongoConverter;
+import org.springframework.data.mapping.BasicMappingContext;
+import org.springframework.data.mapping.model.MappingContext;
+
+import com.mongodb.BasicDBObject;
+import com.mongodb.DBObject;
+
+/**
+ * Unit tests for testing the mapping works with generic types.
+ *
+ * @author Oliver Gierke
+ */
+public class GenericMappingTests {
+
+ MappingContext context;
+ MongoConverter converter;
+
+ @Before
+ public void setUp() {
+ context = new BasicMappingContext(new MongoMappingConfigurationBuilder(null));
+ context.addPersistentEntity(StringWrapper.class);
+ converter = new MappingMongoConverter(context);
+ }
+
+ @Test
+ public void writesGenericTypeCorrectly() {
+
+ StringWrapper wrapper = new StringWrapper();
+ wrapper.container = new Container();
+ wrapper.container.content = "Foo!";
+
+ context.addPersistentEntity(StringWrapper.class);
+
+ DBObject dbObject = new BasicDBObject();
+ converter.write(wrapper, dbObject);
+
+ Object container = dbObject.get("container");
+ assertThat(container, is(notNullValue()));
+ assertTrue(container instanceof DBObject);
+
+ Object content = ((DBObject) container).get("content");
+ assertTrue(content instanceof String);
+ assertThat((String) content, is("Foo!"));
+ }
+
+ @Test
+ public void readsGenericTypeCorrectly() {
+
+ DBObject content = new BasicDBObject("content", "Foo!");
+ BasicDBObject container = new BasicDBObject("container", content);
+
+ StringWrapper result = converter.read(StringWrapper.class, container);
+ assertThat(result.container, is(notNullValue()));
+ assertThat(result.container.content, is("Foo!"));
+ }
+
+ public class StringWrapper extends Wrapper {
+
+ }
+
+ public class Wrapper {
+ Container container;
+ }
+
+ public class Container {
+ T content;
+ }
+}
diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/document/mongodb/mapping/MappingTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/document/mongodb/mapping/MappingTests.java
index 25c614a33..f2d3489b6 100644
--- a/spring-data-mongodb/src/test/java/org/springframework/data/document/mongodb/mapping/MappingTests.java
+++ b/spring-data-mongodb/src/test/java/org/springframework/data/document/mongodb/mapping/MappingTests.java
@@ -34,9 +34,10 @@ import java.util.HashMap;
import java.util.List;
import java.util.Map;
-import static org.hamcrest.Matchers.is;
-import static org.hamcrest.Matchers.notNullValue;
-import static org.junit.Assert.*;
+import static org.hamcrest.CoreMatchers.*;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertThat;
+import static org.junit.Assert.assertEquals;
/**
* @author Jon Brisbin
diff --git a/spring-data-mongodb/template.mf b/spring-data-mongodb/template.mf
index 34f11fe20..55eb4fb9f 100644
--- a/spring-data-mongodb/template.mf
+++ b/spring-data-mongodb/template.mf
@@ -19,6 +19,7 @@ Import-Template:
org.springframework.data.domain.*;version="[1.0.0, 2.0.0)",
org.springframework.data.document.*;version="[1.0.0, 2.0.0)",
org.springframework.data.repository.*;version="[1.0.0, 2.0.0)",
+ org.springframework.data.util.*;version="[1.0.0, 2.0.0)",
org.springframework.expression.*;version="[3.0.0, 4.0.0)",
org.springframework.expression.common.*;version="[3.0.0, 4.0.0)",
org.springframework.expression.spel.standard.*;version="[3.0.0, 4.0.0)",