diff --git a/spring-data-mongodb/pom.xml b/spring-data-mongodb/pom.xml index 06a373655..c3e99515b 100644 --- a/spring-data-mongodb/pom.xml +++ b/spring-data-mongodb/pom.xml @@ -1,170 +1,191 @@ - 4.0.0 - - org.springframework.data - spring-data-document-parent - 1.0.0.BUILD-SNAPSHOT - ../spring-data-document-parent/pom.xml - - spring-data-mongodb - jar - Spring Data MongoDB Support - - - 2.4 - - - + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> + 4.0.0 + + org.springframework.data + spring-data-document-parent + 1.0.0.BUILD-SNAPSHOT + ../spring-data-document-parent/pom.xml + + spring-data-mongodb + jar + Spring Data MongoDB Support - - - org.springframework - spring-beans - - - org.springframework - spring-tx - - - org.springframework - spring-expression - - - org.springframework - spring-test - test - + + 2.4 + - - - org.springframework.data - spring-data-document-core - ${project.version} - - - org.springframework.data - spring-data-commons-core - ${data.commons.version} - - - - - org.mongodb - mongo-java-driver - ${mongo.version} - - - javax.persistence - persistence-api - - - + - - com.mysema.querydsl - querydsl-mongodb - 2.1.1 - true - - - com.google.code.morphia - morphia - - - - - - com.mysema.querydsl - querydsl-apt - 2.1.1 - provided - - - - javax.annotation - jsr250-api - true - + + + org.springframework + spring-beans + + + org.springframework + spring-tx + + + org.springframework + spring-expression + + + org.springframework + spring-test + test + - - javax.annotation - jsr250-api - true - + + + org.springframework.data + spring-data-document-core + ${project.version} + + + org.springframework.data + spring-data-commons-core + ${data.commons.version} + - - - - - org.mockito - mockito-all - test - + + + org.mongodb + mongo-java-driver + ${mongo.version} + + + javax.persistence + persistence-api + + + - - org.hamcrest - hamcrest-all - 1.1 - test - - - - junit - junit - test - - - - joda-time - joda-time - 1.6 - test - + + com.mysema.querydsl + querydsl-mongodb + 2.1.1 + true + + + com.google.code.morphia + morphia + + + - - - - - com.springsource.bundlor - com.springsource.bundlor.maven - - - - com.mysema.maven - maven-apt-plugin - 1.0 - - - generate-test-sources - - test-process - - - target/generated-sources/test-annotations - org.springframework.data.document.mongodb.repository.MongoAnnotationProcessor - - - - - - - - - - querydsl - Mysema QueryDsl - http://source.mysema.com/maven2/releases - - false - - - + + com.mysema.querydsl + querydsl-apt + 2.1.1 + provided + + + + javax.annotation + jsr250-api + true + + + + javax.annotation + jsr250-api + true + + + + + + + org.mockito + mockito-all + test + + + + org.hamcrest + hamcrest-all + 1.1 + test + + + + junit + junit + test + + + + joda-time + joda-time + 1.6 + test + + + + org.slf4j + slf4j-api + test + + + org.slf4j + jcl-over-slf4j + test + + + org.slf4j + slf4j-log4j12 + test + + + log4j + log4j + test + + + + + + + com.springsource.bundlor + com.springsource.bundlor.maven + + + + com.mysema.maven + maven-apt-plugin + 1.0 + + + generate-test-sources + + test-process + + + target/generated-sources/test-annotations + org.springframework.data.document.mongodb.repository.MongoAnnotationProcessor + + + + + + + + + + querydsl + Mysema QueryDsl + http://source.mysema.com/maven2/releases + + false + + + diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/document/mongodb/MongoTemplate.java b/spring-data-mongodb/src/main/java/org/springframework/data/document/mongodb/MongoTemplate.java index fedaee693..7c5159054 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/document/mongodb/MongoTemplate.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/document/mongodb/MongoTemplate.java @@ -57,7 +57,6 @@ import org.springframework.data.document.mongodb.convert.MappingMongoConverter; import org.springframework.data.document.mongodb.convert.MongoConverter; import org.springframework.data.document.mongodb.convert.SimpleMongoConverter; import org.springframework.data.document.mongodb.index.IndexDefinition; -import org.springframework.data.document.mongodb.mapping.Document; import org.springframework.data.document.mongodb.mapping.MongoPersistentEntity; import org.springframework.data.document.mongodb.mapping.event.AfterConvertEvent; import org.springframework.data.document.mongodb.mapping.event.AfterLoadEvent; @@ -67,7 +66,9 @@ import org.springframework.data.document.mongodb.mapping.event.BeforeSaveEvent; import org.springframework.data.document.mongodb.mapping.event.MongoMappingEvent; import org.springframework.data.document.mongodb.query.Query; import org.springframework.data.document.mongodb.query.Update; +import org.springframework.data.mapping.context.MappingContextAware; import org.springframework.data.mapping.model.MappingContext; +import org.springframework.data.mapping.model.PersistentEntity; import org.springframework.jca.cci.core.ConnectionCallback; import org.springframework.util.Assert; @@ -79,7 +80,7 @@ import org.springframework.util.Assert; * @author Mark Pollack * @author Oliver Gierke */ -public class MongoTemplate implements InitializingBean, MongoOperations, ApplicationContextAware, ApplicationEventPublisherAware { +public class MongoTemplate implements InitializingBean, MongoOperations, ApplicationContextAware, ApplicationEventPublisherAware, MappingContextAware { private static final Log LOGGER = LogFactory.getLog(MongoTemplate.class); @@ -98,6 +99,7 @@ public class MongoTemplate implements InitializingBean, MongoOperations, Applica private WriteResultChecking writeResultChecking = WriteResultChecking.NONE; private MongoConverter mongoConverter; + private MappingContext mappingContext; private final Mongo mongo; private final MongoExceptionTranslator exceptionTranslator = new MongoExceptionTranslator(); @@ -202,6 +204,10 @@ public class MongoTemplate implements InitializingBean, MongoOperations, Applica this.eventPublisher = applicationEventPublisher; } + public void setMappingContext(MappingContext mappingContext) { + this.mappingContext = mappingContext; + } + /** * Sets the username to use to connect to the Mongo database * @@ -508,19 +514,11 @@ public class MongoTemplate implements InitializingBean, MongoOperations, Applica // Find methods that take a Query to express the query and that return a List of objects. public List find(Query query, Class targetClass) { - String collName = getEntityCollection(targetClass); - if (null == collName) { - collName = getRequiredDefaultCollectionName(); - } - return find(collName, query, targetClass); + return find(getEntityCollection(targetClass), query, targetClass); } public List find(Query query, Class targetClass, MongoReader reader) { - String collName = getEntityCollection(targetClass); - if (null == collName) { - collName = getRequiredDefaultCollectionName(); - } - return find(collName, query, targetClass, reader); + return find(getEntityCollection(targetClass), query, targetClass, reader); } public List find(String collectionName, final Query query, Class targetClass) { @@ -585,11 +583,7 @@ public class MongoTemplate implements InitializingBean, MongoOperations, Applica * @see org.springframework.data.document.mongodb.MongoOperations#insert(java.lang.Object) */ public void insert(Object objectToSave) { - String collName = getEntityCollection(objectToSave); - if (null == collName) { - collName = getRequiredDefaultCollectionName(); - } - insert(collName, objectToSave); + insert(getEntityCollection(objectToSave), objectToSave); } /* (non-Javadoc) @@ -603,11 +597,7 @@ public class MongoTemplate implements InitializingBean, MongoOperations, Applica * @see org.springframework.data.document.mongodb.MongoOperations#insert(T, org.springframework.data.document.mongodb.MongoWriter) */ public void insert(T objectToSave, MongoWriter writer) { - String collName = getEntityCollection(objectToSave); - if (null == collName) { - collName = getDefaultCollectionName(); - } - insert(collName, objectToSave, writer); + insert(getEntityCollection(objectToSave), objectToSave, writer); } /* (non-Javadoc) @@ -1103,7 +1093,7 @@ public class MongoTemplate implements InitializingBean, MongoOperations, Applica return name; } - private String getEntityCollection(Object obj) { + private String getEntityCollection(T obj) { if (null != obj) { return getEntityCollection(obj.getClass()); } @@ -1111,26 +1101,15 @@ public class MongoTemplate implements InitializingBean, MongoOperations, Applica return null; } - private String getEntityCollection(Class clazz) { - if (mongoConverter instanceof MappingMongoConverter) { - MappingContext ctx = ((MappingMongoConverter) mongoConverter).getMappingContext(); - MongoPersistentEntity entity = (MongoPersistentEntity) ctx.getPersistentEntity(clazz); - if (null != entity) { - return entity.getCollection(); + private String getEntityCollection(Class clazz) { + if (null != mappingContext) { + PersistentEntity entity = mappingContext.getPersistentEntity(clazz); + if (null != entity && entity instanceof MongoPersistentEntity) { + return ((MongoPersistentEntity) entity).getCollection(); } } - // Entity hasn't yet been added, try and figure it out anyway - if (clazz.isAnnotationPresent(Document.class)) { - Document doc = clazz.getAnnotation(Document.class); - if (!"".equals(doc.collection())) { - return doc.collection(); - } - - // Default to simple name - return clazz.getSimpleName().toLowerCase(); - } - - return null; + // Otherwise, return the default for this template. + return getRequiredDefaultCollectionName(); } /** diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/document/mongodb/config/MongoMappingConverterParser.java b/spring-data-mongodb/src/main/java/org/springframework/data/document/mongodb/config/MongoMappingConverterParser.java index 5dcc67b56..4d44f1181 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/document/mongodb/config/MongoMappingConverterParser.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/document/mongodb/config/MongoMappingConverterParser.java @@ -33,8 +33,9 @@ import org.springframework.core.type.filter.AnnotationTypeFilter; import org.springframework.data.annotation.Persistent; import org.springframework.data.document.mongodb.convert.MappingMongoConverter; import org.springframework.data.document.mongodb.mapping.Document; -import org.springframework.data.document.mongodb.mapping.MongoPersistentEntityIndexCreator; import org.springframework.data.document.mongodb.mapping.MongoMappingContext; +import org.springframework.data.document.mongodb.mapping.MongoPersistentEntityIndexCreator; +import org.springframework.data.mapping.context.MappingContextAwareBeanPostProcessor; import org.springframework.util.StringUtils; import org.w3c.dom.Element; @@ -44,77 +45,83 @@ import org.w3c.dom.Element; */ public class MongoMappingConverterParser extends AbstractBeanDefinitionParser { - static final String MAPPING_CONTEXT = "mappingContext"; - private static final String INDEX_HELPER = "indexCreationHelper"; - private static final String TEMPLATE = "mongoTemplate"; - private static final String BASE_PACKAGE = "base-package"; + static final String MAPPING_CONTEXT = "mappingContext"; - @Override - protected String resolveId(Element element, AbstractBeanDefinition definition, ParserContext parserContext) throws BeanDefinitionStoreException { - String id = super.resolveId(element, definition, parserContext); - return StringUtils.hasText(id) ? id : "mappingConverter"; - } + private static final String INDEX_HELPER = "indexCreationHelper"; + private static final String TEMPLATE = "mongoTemplate"; + private static final String POST_PROCESSOR = "mappingContextAwareBeanPostProcessor"; - @Override - protected AbstractBeanDefinition parseInternal(Element element, ParserContext parserContext) { - BeanDefinitionRegistry registry = parserContext.getRegistry(); + private static final String BASE_PACKAGE = "base-package"; - String ctxRef = element.getAttribute("mapping-context-ref"); - if (!StringUtils.hasText(ctxRef)) { - BeanDefinitionBuilder mappingContextBuilder = BeanDefinitionBuilder.genericBeanDefinition(MongoMappingContext.class); - - Set classesToAdd = getInititalEntityClasses(element, mappingContextBuilder); - if (classesToAdd != null) { - mappingContextBuilder.addPropertyValue("initialEntitySet", classesToAdd); - } - - registry.registerBeanDefinition(MAPPING_CONTEXT, mappingContextBuilder.getBeanDefinition()); - ctxRef = MAPPING_CONTEXT; - } + @Override + protected String resolveId(Element element, AbstractBeanDefinition definition, ParserContext parserContext) throws BeanDefinitionStoreException { + String id = super.resolveId(element, definition, parserContext); + return StringUtils.hasText(id) ? id : "mappingConverter"; + } - BeanDefinitionBuilder converterBuilder = BeanDefinitionBuilder.genericBeanDefinition(MappingMongoConverter.class); - converterBuilder.addPropertyReference("mappingContext", ctxRef); + @Override + protected AbstractBeanDefinition parseInternal(Element element, ParserContext parserContext) { + BeanDefinitionRegistry registry = parserContext.getRegistry(); - String autowire = element.getAttribute("autowire"); - if (StringUtils.hasText(autowire)) { - converterBuilder.addPropertyValue("autowirePersistentBeans", Boolean.parseBoolean(autowire)); - } + String ctxRef = element.getAttribute("mapping-context-ref"); + if (!StringUtils.hasText(ctxRef)) { + BeanDefinitionBuilder mappingContextBuilder = BeanDefinitionBuilder.genericBeanDefinition(MongoMappingContext.class); - // Need a reference to a Mongo instance - String mongoRef = element.getAttribute("mongo-ref"); - converterBuilder.addPropertyReference("mongo", StringUtils.hasText(mongoRef) ? mongoRef : "mongo"); + Set classesToAdd = getInititalEntityClasses(element, mappingContextBuilder); + if (classesToAdd != null) { + mappingContextBuilder.addPropertyValue("initialEntitySet", classesToAdd); + } - try { - registry.getBeanDefinition(INDEX_HELPER); - } catch (NoSuchBeanDefinitionException ignored) { - String templateRef = element.getAttribute("mongo-template-ref"); - BeanDefinitionBuilder indexHelperBuilder = BeanDefinitionBuilder.genericBeanDefinition(MongoPersistentEntityIndexCreator.class); - indexHelperBuilder.addConstructorArgValue(new RuntimeBeanReference(ctxRef)); - indexHelperBuilder.addConstructorArgValue(new RuntimeBeanReference(StringUtils.hasText(templateRef) ? templateRef : TEMPLATE)); - registry.registerBeanDefinition(INDEX_HELPER, indexHelperBuilder.getBeanDefinition()); - } + registry.registerBeanDefinition(MAPPING_CONTEXT, mappingContextBuilder.getBeanDefinition()); + ctxRef = MAPPING_CONTEXT; + } - return converterBuilder.getBeanDefinition(); - } - - - public Set getInititalEntityClasses(Element element, BeanDefinitionBuilder builder) { - - String basePackage = element.getAttribute(BASE_PACKAGE); - - if (!StringUtils.hasText(basePackage)) { - return null; - } - - ClassPathScanningCandidateComponentProvider componentProvider = new ClassPathScanningCandidateComponentProvider(false); - componentProvider.addIncludeFilter(new AnnotationTypeFilter(Document.class)); - componentProvider.addIncludeFilter(new AnnotationTypeFilter(Persistent.class)); - - Set classes = new ManagedSet(); - for (BeanDefinition candidate : componentProvider.findCandidateComponents(basePackage)) { - classes.add(candidate.getBeanClassName()); - } - - return classes; - } + try { + registry.getBeanDefinition(POST_PROCESSOR); + } catch (NoSuchBeanDefinitionException ignored) { + BeanDefinitionBuilder postProcBuilder = BeanDefinitionBuilder.genericBeanDefinition(MappingContextAwareBeanPostProcessor.class); + postProcBuilder.addPropertyValue("mappingContextBeanName", ctxRef); + registry.registerBeanDefinition(POST_PROCESSOR, postProcBuilder.getBeanDefinition()); + } + + BeanDefinitionBuilder converterBuilder = BeanDefinitionBuilder.genericBeanDefinition(MappingMongoConverter.class); + converterBuilder.addPropertyReference("mappingContext", ctxRef); + + // Need a reference to a Mongo instance + String mongoRef = element.getAttribute("mongo-ref"); + converterBuilder.addPropertyReference("mongo", StringUtils.hasText(mongoRef) ? mongoRef : "mongo"); + + try { + registry.getBeanDefinition(INDEX_HELPER); + } catch (NoSuchBeanDefinitionException ignored) { + String templateRef = element.getAttribute("mongo-template-ref"); + BeanDefinitionBuilder indexHelperBuilder = BeanDefinitionBuilder.genericBeanDefinition(MongoPersistentEntityIndexCreator.class); + indexHelperBuilder.addConstructorArgValue(new RuntimeBeanReference(ctxRef)); + indexHelperBuilder.addConstructorArgValue(new RuntimeBeanReference(StringUtils.hasText(templateRef) ? templateRef : TEMPLATE)); + registry.registerBeanDefinition(INDEX_HELPER, indexHelperBuilder.getBeanDefinition()); + } + + return converterBuilder.getBeanDefinition(); + } + + + public Set getInititalEntityClasses(Element element, BeanDefinitionBuilder builder) { + + String basePackage = element.getAttribute(BASE_PACKAGE); + + if (!StringUtils.hasText(basePackage)) { + return null; + } + + ClassPathScanningCandidateComponentProvider componentProvider = new ClassPathScanningCandidateComponentProvider(false); + componentProvider.addIncludeFilter(new AnnotationTypeFilter(Document.class)); + componentProvider.addIncludeFilter(new AnnotationTypeFilter(Persistent.class)); + + Set classes = new ManagedSet(); + for (BeanDefinition candidate : componentProvider.findCandidateComponents(basePackage)) { + classes.add(candidate.getBeanClassName()); + } + + return classes; + } } 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 2668d6957..84ae4ca3f 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 @@ -70,617 +70,597 @@ import org.springframework.expression.spel.support.StandardEvaluationContext; */ public class MappingMongoConverter implements MongoConverter, ApplicationContextAware, InitializingBean { - private static final String CUSTOM_TYPE_KEY = "_class"; - @SuppressWarnings({"unchecked"}) - private static final List> MONGO_TYPES = Arrays.asList(Number.class, Date.class, String.class, DBObject.class); - protected static final Log log = LogFactory.getLog(MappingMongoConverter.class); - - protected final GenericConversionService conversionService = ConversionServiceFactory.createDefaultConversionService(); - protected final Map, Class> customTypeMapping = new HashMap, Class>(); - protected SpelExpressionParser spelExpressionParser = new SpelExpressionParser(); - protected MappingContext mappingContext; - protected ApplicationContext applicationContext; - protected boolean autowirePersistentBeans = false; - protected boolean useFieldAccessOnly = true; - protected Mongo mongo; - protected String defaultDatabase; - - public MappingMongoConverter() { - initializeConverters(); - } - - public MappingMongoConverter(MappingContext mappingContext) { - this.mappingContext = mappingContext; - initializeConverters(); - } - - public MappingMongoConverter(MappingContext mappingContext, List> converters) { - this.mappingContext = mappingContext; - if (null != converters) { - for (Converter c : converters) { - registerConverter(c); - conversionService.addConverter(c); - } - } - initializeConverters(); - } - - /** - * Inspects the given {@link Converter} for the types it can convert and registers the pair for custom type conversion - * in case the target type is a Mongo basic type. - * - * @param converter - */ - private void registerConverter(Converter converter) { - Class[] arguments = GenericTypeResolver.resolveTypeArguments(converter.getClass(), Converter.class); - if (MONGO_TYPES.contains(arguments[1])) { - customTypeMapping.put(arguments[0], arguments[1]); - } - } - - public MappingContext getMappingContext() { - return mappingContext; - } - - public void setMappingContext(MappingContext mappingContext) { - this.mappingContext = mappingContext; - } - - public Mongo getMongo() { - return mongo; - } - - public void setMongo(Mongo mongo) { - this.mongo = mongo; - } - - public String getDefaultDatabase() { - return defaultDatabase; - } - - public void setDefaultDatabase(String defaultDatabase) { - this.defaultDatabase = defaultDatabase; - } - - public boolean isAutowirePersistentBeans() { - return autowirePersistentBeans; - } - - public void setAutowirePersistentBeans(boolean autowirePersistentBeans) { - this.autowirePersistentBeans = autowirePersistentBeans; - } - - public boolean isUseFieldAccessOnly() { - return useFieldAccessOnly; - } - - public void setUseFieldAccessOnly(boolean useFieldAccessOnly) { - this.useFieldAccessOnly = useFieldAccessOnly; - } - - public T convertObjectId(ObjectId id, Class targetType) { - return conversionService.convert(id, targetType); - } - - public ObjectId convertObjectId(Object id) { - return conversionService.convert(id, ObjectId.class); - } - - @SuppressWarnings({"unchecked", "rawtypes"}) - public S read(Class clazz, final DBObject dbo) { - if (null == dbo) { - return null; - } - - if ((clazz.isArray() - || (clazz.isAssignableFrom(Collection.class) - || clazz.isAssignableFrom(List.class))) - && dbo instanceof BasicDBList) { - List l = new ArrayList(); - BasicDBList dbList = (BasicDBList) dbo; - for (Object o : dbList) { - if (o instanceof DBObject) { - Object newObj = read(clazz.getComponentType(), (DBObject) o); - if (newObj.getClass().isAssignableFrom(clazz.getComponentType())) { - l.add(newObj); - } else { - l.add(conversionService.convert(newObj, clazz.getComponentType())); - } - } else { - l.add(o); - } - } - return conversionService.convert(l, clazz); - } - - // Retrieve persistent entity info - 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() { - public Object getParameterValue(PreferredConstructor.Parameter parameter) { - String name = parameter.getName(); - Class type = parameter.getType(); - Object obj = dbo.get(name); - if (obj instanceof DBRef) { - ctorParamNames.add(name); - return read(type, ((DBRef) obj).fetch()); - } else if (obj instanceof DBObject) { - ctorParamNames.add(name); - return read(type, ((DBObject) obj)); - } else if (null != obj && obj.getClass().isAssignableFrom(type)) { - ctorParamNames.add(name); - return obj; - } else if (null != obj) { - ctorParamNames.add(name); - return conversionService.convert(obj, type); - } - - return null; - } - }, spelCtx); - - // Set the ID - PersistentProperty idProperty = entity.getIdProperty(); - if (dbo.containsField("_id") && null != idProperty) { - Object idObj = dbo.get("_id"); - try { - MappingBeanHelper.setProperty(instance, idProperty, idObj, useFieldAccessOnly); - } catch (IllegalAccessException e) { - throw new MappingException(e.getMessage(), e); - } catch (InvocationTargetException e) { - throw new MappingException(e.getMessage(), e); - } - } - - // Set properties not already set in the constructor - entity.doWithProperties(new PropertyHandler() { - public void doWithPersistentProperty(PersistentProperty prop) { - if (ctorParamNames.contains(prop.getName())) { - return; - } - - 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); - } - } - }); - - // Handle associations - entity.doWithAssociations(new AssociationHandler() { - public void doWithAssociation(Association association) { - PersistentProperty inverseProp = association.getInverse(); - Object obj = getValueInternal(inverseProp, dbo, spelCtx, inverseProp.getValueAnnotation()); - try { - MappingBeanHelper.setProperty(instance, inverseProp, obj); - } catch (IllegalAccessException e) { - throw new MappingException(e.getMessage(), e); - } catch (InvocationTargetException e) { - throw new MappingException(e.getMessage(), e); - } - } - }); - - if (null != applicationContext && autowirePersistentBeans) { - applicationContext.getAutowireCapableBeanFactory().autowireBean(instance); - } - if (instance instanceof InitializingBean) { - try { - ((InitializingBean) instance).afterPropertiesSet(); - } catch (Exception e) { - throw new MappingException(e.getMessage(), e); - } - } - - return instance; - } - - public void write(final Object obj, final DBObject dbo) { - if (null == obj) { - return; - } - - 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()); - if (null == entity) { - // We can't map this entity for some reason - throw new MappingException("Unable to map entity " + obj); - } - } - - // Write the ID - final PersistentProperty idProperty = entity.getIdProperty(); - if (!dbo.containsField("_id") && null != idProperty) { - Object idObj = null; - try { - idObj = MappingBeanHelper.getProperty(obj, idProperty, ObjectId.class, useFieldAccessOnly); - } catch (IllegalAccessException e) { - throw new MappingException(e.getMessage(), e); - } catch (InvocationTargetException e) { - throw new MappingException(e.getMessage(), e); - } - - if (null != idObj) { - dbo.put("_id", idObj); - } - } - - // Write the properties - entity.doWithProperties(new PropertyHandler() { - public void doWithPersistentProperty(PersistentProperty prop) { - String name = prop.getName(); - Class type = prop.getType(); - Object propertyObj = null; - try { - propertyObj = MappingBeanHelper.getProperty(obj, prop, type, useFieldAccessOnly); - } catch (IllegalAccessException e) { - throw new MappingException(e.getMessage(), e); - } catch (InvocationTargetException e) { - throw new MappingException(e.getMessage(), e); - } - if (null != propertyObj) { - if (!MappingBeanHelper.isSimpleType(propertyObj.getClass())) { - writePropertyInternal(prop, propertyObj, dbo); - } else { - dbo.put(name, propertyObj); - } - } - } - }); - - entity.doWithAssociations(new AssociationHandler() { - public void doWithAssociation(Association association) { - PersistentProperty inverseProp = association.getInverse(); - Class type = inverseProp.getType(); - Object propertyObj = null; - try { - propertyObj = MappingBeanHelper.getProperty(obj, inverseProp, type, useFieldAccessOnly); - } catch (IllegalAccessException e) { - throw new MappingException(e.getMessage(), e); - } catch (InvocationTargetException e) { - throw new MappingException(e.getMessage(), e); - } - if (null != propertyObj) { - writePropertyInternal(inverseProp, propertyObj, dbo); - } - } - }); - } - - public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { - 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); - } - if (!conversionService.canConvert(ObjectId.class, BigInteger.class)) { - 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(); - Class type = prop.getType(); - if (prop.isCollection()) { - BasicDBList dbList = new BasicDBList(); - Collection coll; - if (type.isArray()) { - coll = Arrays.asList((Object[]) obj); - } else { - coll = (Collection) obj; - } - for (Object propObjItem : coll) { - if (null != dbref) { - DBRef dbRef = createDBRef(propObjItem, dbref); - dbList.add(dbRef); - } else if (type.isArray() && MappingBeanHelper.isSimpleType(type.getComponentType())) { - dbList.add(propObjItem); - } else { - BasicDBObject propDbObj = new BasicDBObject(); - write(propObjItem, propDbObj, mappingContext.getPersistentEntity(prop.getTypeInformation())); - dbList.add(propDbObj); - } - } - dbo.put(name, dbList); - return; - } - - if (null != obj && obj instanceof Map) { - BasicDBObject mapDbObj = new BasicDBObject(); - writeMapInternal((Map) obj, mapDbObj); - dbo.put(name, mapDbObj); - return; - } - - if (null != dbref) { - DBRef dbRefObj = createDBRef(obj, dbref); - if (null != dbRefObj) { - dbo.put(name, dbRefObj); - return; - } - } - - // Lookup potential custom target type - Class basicTargetType = customTypeMapping.get(obj.getClass()); - - 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); - - } - - protected void writeMapInternal(Map obj, DBObject dbo) { - for (Map.Entry entry : obj.entrySet()) { - Object key = entry.getKey(); - Object val = entry.getValue(); - if (MappingBeanHelper.isSimpleType(key.getClass())) { - String simpleKey = conversionService.convert(key, String.class); - if (MappingBeanHelper.isSimpleType(val.getClass())) { - dbo.put(simpleKey, val); - } else { - DBObject newDbo = new BasicDBObject(); - Class componentType = val.getClass(); - if (componentType.isArray() - || componentType.isAssignableFrom(Collection.class) - || componentType.isAssignableFrom(List.class)) { - Class ctype = val.getClass().getComponentType(); - dbo.put("_class", (null != ctype ? ctype.getName() : componentType.getName())); - } else { - dbo.put("_class", componentType.getName()); - } - write(val, newDbo); - dbo.put(simpleKey, newDbo); - } - } else { - throw new MappingException("Cannot use a complex object as a key value."); - } - } - } - - protected DBRef createDBRef(Object target, org.springframework.data.document.mongodb.mapping.DBRef dbref) { - PersistentEntity targetEntity = mappingContext.getPersistentEntity(target.getClass()); - if (null == targetEntity || null == targetEntity.getIdProperty()) { - return null; - } - - PersistentProperty idProperty = targetEntity.getIdProperty(); - ObjectId id = null; - try { - id = MappingBeanHelper.getProperty(target, idProperty, ObjectId.class, useFieldAccessOnly); - if (null == id) { - throw new MappingException("Cannot create a reference to an object with a NULL id."); - } - } catch (IllegalAccessException e) { - throw new MappingException(e.getMessage(), e); - } catch (InvocationTargetException e) { - throw new MappingException(e.getMessage(), e); - } - - String collection = dbref.collection(); - if ("".equals(collection)) { - collection = targetEntity.getType().getSimpleName().toLowerCase(); - } - - String dbname = dbref.db(); - if ("".equals(dbname)) { - dbname = defaultDatabase; - } - - DB db = mongo.getDB(dbname); - return new DBRef(db, collection, id); - } - - @SuppressWarnings({"unchecked"}) - protected Object getValueInternal(PersistentProperty prop, DBObject dbo, StandardEvaluationContext ctx, Value spelExpr) { - String name = prop.getName(); - Object o; - if (null != spelExpr) { - Expression x = spelExpressionParser.parseExpression(spelExpr.value()); - o = x.getValue(ctx); - } else { - DBObject from = dbo; - if (dbo instanceof DBRef) { - from = ((DBRef) dbo).fetch(); - } - Object dbObj = from.get(name); - if (dbObj instanceof DBObject) { - 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()) { - 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.getMapValueType()), (DBObject) entry.getValue())); - } else { - m.put(entry.getKey(), entry.getValue()); - } - } - return m; - } else if (prop.isArray() && dbObj instanceof BasicDBObject && ((DBObject) dbObj).keySet().size() == 0) { - // It's empty - 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()); - for (int i = 0; i < dbObjList.size(); i++) { - Object dbObjItem = dbObjList.get(i); - if (dbObjItem instanceof DBRef) { - items[i] = read(prop.getComponentType(), ((DBRef) dbObjItem).fetch()); - } else if (dbObjItem instanceof DBObject) { - items[i] = read(prop.getComponentType(), (DBObject) dbObjItem); - } else { - items[i] = dbObjItem; - } - } - return Arrays.asList(items); - } - - Class toType = findTypeToBeUsed((DBObject) dbObj); - - // It's a complex object, have to read it in - if (toType != null) { - dbo.removeField(CUSTOM_TYPE_KEY); - o = read(toType, (DBObject) dbObj); - } else { - o = read(mappingContext.getPersistentEntity(prop.getTypeInformation()), (DBObject) dbObj); - } - } else { - o = dbObj; - } - } - return o; - } - - /** - * Returns the type to be used to convert the DBObject given to. - * - * @param dbObject - * @return - */ - protected Class findTypeToBeUsed(DBObject dbObject) { - Object classToBeUsed = dbObject.get(CUSTOM_TYPE_KEY); - - if (classToBeUsed == null) { - return null; - } - - try { - return Class.forName(classToBeUsed.toString()); - } catch (ClassNotFoundException e) { - throw new MappingException(e.getMessage(), e); - } - } - - public void afterPropertiesSet() throws Exception { - } - - /** - * Simple singleton to convert {@link ObjectId}s to their {@link String} representation. - * - * @author Oliver Gierke - */ - public static enum ObjectIdToStringConverter implements Converter { - INSTANCE; - - public String convert(ObjectId id) { - return id.toString(); - } - } - - /** - * Simple singleton to convert {@link String}s to their {@link ObjectId} representation. - * - * @author Oliver Gierke - */ - public static enum StringToObjectIdConverter implements Converter { - INSTANCE; - - public ObjectId convert(String source) { - return new ObjectId(source); - } - } - - /** - * Simple singleton to convert {@link ObjectId}s to their {@link java.math.BigInteger} representation. - * - * @author Oliver Gierke - */ - public static enum ObjectIdToBigIntegerConverter implements Converter { - INSTANCE; - - public BigInteger convert(ObjectId source) { - return new BigInteger(source.toString(), 16); - } - } - - /** - * Simple singleton to convert {@link BigInteger}s to their {@link ObjectId} representation. - * - * @author Oliver Gierke - */ - public static enum BigIntegerToIdConverter implements Converter { - INSTANCE; - - public ObjectId convert(BigInteger source) { - return new ObjectId(source.toString(16)); - } - } - - protected class PersistentPropertyWrapper { - private final PersistentProperty property; - private final DBObject target; - - public PersistentPropertyWrapper(PersistentProperty property, DBObject target) { - this.property = property; - this.target = target; - } - - public PersistentProperty getProperty() { - return property; - } - - public DBObject getTarget() { - return target; - } - } + private static final String CUSTOM_TYPE_KEY = "_class"; + @SuppressWarnings({"unchecked"}) + private static final List> MONGO_TYPES = Arrays.asList(Number.class, Date.class, String.class, DBObject.class); + protected static final Log log = LogFactory.getLog(MappingMongoConverter.class); + + protected final GenericConversionService conversionService = ConversionServiceFactory.createDefaultConversionService(); + protected final Map, Class> customTypeMapping = new HashMap, Class>(); + protected SpelExpressionParser spelExpressionParser = new SpelExpressionParser(); + protected MappingContext mappingContext; + protected ApplicationContext applicationContext; + protected boolean useFieldAccessOnly = true; + protected Mongo mongo; + protected String defaultDatabase; + + public MappingMongoConverter() { + initializeConverters(); + } + + public MappingMongoConverter(MappingContext mappingContext) { + this.mappingContext = mappingContext; + initializeConverters(); + } + + public MappingMongoConverter(MappingContext mappingContext, List> converters) { + this.mappingContext = mappingContext; + if (null != converters) { + for (Converter c : converters) { + registerConverter(c); + conversionService.addConverter(c); + } + } + initializeConverters(); + } + + /** + * Inspects the given {@link Converter} for the types it can convert and registers the pair for custom type conversion + * in case the target type is a Mongo basic type. + * + * @param converter + */ + private void registerConverter(Converter converter) { + Class[] arguments = GenericTypeResolver.resolveTypeArguments(converter.getClass(), Converter.class); + if (MONGO_TYPES.contains(arguments[1])) { + customTypeMapping.put(arguments[0], arguments[1]); + } + } + + public MappingContext getMappingContext() { + return mappingContext; + } + + public void setMappingContext(MappingContext mappingContext) { + this.mappingContext = mappingContext; + } + + public Mongo getMongo() { + return mongo; + } + + public void setMongo(Mongo mongo) { + this.mongo = mongo; + } + + public String getDefaultDatabase() { + return defaultDatabase; + } + + public void setDefaultDatabase(String defaultDatabase) { + this.defaultDatabase = defaultDatabase; + } + + public boolean isUseFieldAccessOnly() { + return useFieldAccessOnly; + } + + public void setUseFieldAccessOnly(boolean useFieldAccessOnly) { + this.useFieldAccessOnly = useFieldAccessOnly; + } + + public T convertObjectId(ObjectId id, Class targetType) { + return conversionService.convert(id, targetType); + } + + public ObjectId convertObjectId(Object id) { + return conversionService.convert(id, ObjectId.class); + } + + @SuppressWarnings({"unchecked", "rawtypes"}) + public S read(Class clazz, final DBObject dbo) { + if (null == dbo) { + return null; + } + + if ((clazz.isArray() + || (clazz.isAssignableFrom(Collection.class) + || clazz.isAssignableFrom(List.class))) + && dbo instanceof BasicDBList) { + List l = new ArrayList(); + BasicDBList dbList = (BasicDBList) dbo; + for (Object o : dbList) { + if (o instanceof DBObject) { + Object newObj = read(clazz.getComponentType(), (DBObject) o); + if (newObj.getClass().isAssignableFrom(clazz.getComponentType())) { + l.add(newObj); + } else { + l.add(conversionService.convert(newObj, clazz.getComponentType())); + } + } else { + l.add(o); + } + } + return conversionService.convert(l, clazz); + } + + // Retrieve persistent entity info + 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() { + public Object getParameterValue(PreferredConstructor.Parameter parameter) { + String name = parameter.getName(); + Class type = parameter.getType(); + Object obj = dbo.get(name); + if (obj instanceof DBRef) { + ctorParamNames.add(name); + return read(type, ((DBRef) obj).fetch()); + } else if (obj instanceof DBObject) { + ctorParamNames.add(name); + return read(type, ((DBObject) obj)); + } else if (null != obj && obj.getClass().isAssignableFrom(type)) { + ctorParamNames.add(name); + return obj; + } else if (null != obj) { + ctorParamNames.add(name); + return conversionService.convert(obj, type); + } + + return null; + } + }, spelCtx); + + // Set the ID + PersistentProperty idProperty = entity.getIdProperty(); + if (dbo.containsField("_id") && null != idProperty) { + Object idObj = dbo.get("_id"); + try { + MappingBeanHelper.setProperty(instance, idProperty, idObj, useFieldAccessOnly); + } catch (IllegalAccessException e) { + throw new MappingException(e.getMessage(), e); + } catch (InvocationTargetException e) { + throw new MappingException(e.getMessage(), e); + } + } + + // Set properties not already set in the constructor + entity.doWithProperties(new PropertyHandler() { + public void doWithPersistentProperty(PersistentProperty prop) { + if (ctorParamNames.contains(prop.getName())) { + return; + } + + 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); + } + } + }); + + // Handle associations + entity.doWithAssociations(new AssociationHandler() { + public void doWithAssociation(Association association) { + PersistentProperty inverseProp = association.getInverse(); + Object obj = getValueInternal(inverseProp, dbo, spelCtx, inverseProp.getValueAnnotation()); + try { + MappingBeanHelper.setProperty(instance, inverseProp, obj); + } catch (IllegalAccessException e) { + throw new MappingException(e.getMessage(), e); + } catch (InvocationTargetException e) { + throw new MappingException(e.getMessage(), e); + } + } + }); + + return instance; + } + + public void write(final Object obj, final DBObject dbo) { + if (null == obj) { + return; + } + + 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()); + if (null == entity) { + // We can't map this entity for some reason + throw new MappingException("Unable to map entity " + obj); + } + } + + // Write the ID + final PersistentProperty idProperty = entity.getIdProperty(); + if (!dbo.containsField("_id") && null != idProperty) { + Object idObj = null; + try { + idObj = MappingBeanHelper.getProperty(obj, idProperty, ObjectId.class, useFieldAccessOnly); + } catch (IllegalAccessException e) { + throw new MappingException(e.getMessage(), e); + } catch (InvocationTargetException e) { + throw new MappingException(e.getMessage(), e); + } + + if (null != idObj) { + dbo.put("_id", idObj); + } + } + + // Write the properties + entity.doWithProperties(new PropertyHandler() { + public void doWithPersistentProperty(PersistentProperty prop) { + String name = prop.getName(); + Class type = prop.getType(); + Object propertyObj = null; + try { + propertyObj = MappingBeanHelper.getProperty(obj, prop, type, useFieldAccessOnly); + } catch (IllegalAccessException e) { + throw new MappingException(e.getMessage(), e); + } catch (InvocationTargetException e) { + throw new MappingException(e.getMessage(), e); + } + if (null != propertyObj) { + if (!MappingBeanHelper.isSimpleType(propertyObj.getClass())) { + writePropertyInternal(prop, propertyObj, dbo); + } else { + dbo.put(name, propertyObj); + } + } + } + }); + + entity.doWithAssociations(new AssociationHandler() { + public void doWithAssociation(Association association) { + PersistentProperty inverseProp = association.getInverse(); + Class type = inverseProp.getType(); + Object propertyObj = null; + try { + propertyObj = MappingBeanHelper.getProperty(obj, inverseProp, type, useFieldAccessOnly); + } catch (IllegalAccessException e) { + throw new MappingException(e.getMessage(), e); + } catch (InvocationTargetException e) { + throw new MappingException(e.getMessage(), e); + } + if (null != propertyObj) { + writePropertyInternal(inverseProp, propertyObj, dbo); + } + } + }); + } + + public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { + 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); + } + if (!conversionService.canConvert(ObjectId.class, BigInteger.class)) { + 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(); + Class type = prop.getType(); + if (prop.isCollection()) { + BasicDBList dbList = new BasicDBList(); + Collection coll; + if (type.isArray()) { + coll = Arrays.asList((Object[]) obj); + } else { + coll = (Collection) obj; + } + for (Object propObjItem : coll) { + if (null != dbref) { + DBRef dbRef = createDBRef(propObjItem, dbref); + dbList.add(dbRef); + } else if (type.isArray() && MappingBeanHelper.isSimpleType(type.getComponentType())) { + dbList.add(propObjItem); + } else { + BasicDBObject propDbObj = new BasicDBObject(); + write(propObjItem, propDbObj, mappingContext.getPersistentEntity(prop.getTypeInformation())); + dbList.add(propDbObj); + } + } + dbo.put(name, dbList); + return; + } + + if (null != obj && obj instanceof Map) { + BasicDBObject mapDbObj = new BasicDBObject(); + writeMapInternal((Map) obj, mapDbObj); + dbo.put(name, mapDbObj); + return; + } + + if (null != dbref) { + DBRef dbRefObj = createDBRef(obj, dbref); + if (null != dbRefObj) { + dbo.put(name, dbRefObj); + return; + } + } + + // Lookup potential custom target type + Class basicTargetType = customTypeMapping.get(obj.getClass()); + + 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); + + } + + protected void writeMapInternal(Map obj, DBObject dbo) { + for (Map.Entry entry : obj.entrySet()) { + Object key = entry.getKey(); + Object val = entry.getValue(); + if (MappingBeanHelper.isSimpleType(key.getClass())) { + String simpleKey = conversionService.convert(key, String.class); + if (MappingBeanHelper.isSimpleType(val.getClass())) { + dbo.put(simpleKey, val); + } else { + DBObject newDbo = new BasicDBObject(); + Class componentType = val.getClass(); + if (componentType.isArray() + || componentType.isAssignableFrom(Collection.class) + || componentType.isAssignableFrom(List.class)) { + Class ctype = val.getClass().getComponentType(); + dbo.put("_class", (null != ctype ? ctype.getName() : componentType.getName())); + } else { + dbo.put("_class", componentType.getName()); + } + write(val, newDbo); + dbo.put(simpleKey, newDbo); + } + } else { + throw new MappingException("Cannot use a complex object as a key value."); + } + } + } + + protected DBRef createDBRef(Object target, org.springframework.data.document.mongodb.mapping.DBRef dbref) { + PersistentEntity targetEntity = mappingContext.getPersistentEntity(target.getClass()); + if (null == targetEntity || null == targetEntity.getIdProperty()) { + return null; + } + + PersistentProperty idProperty = targetEntity.getIdProperty(); + ObjectId id = null; + try { + id = MappingBeanHelper.getProperty(target, idProperty, ObjectId.class, useFieldAccessOnly); + if (null == id) { + throw new MappingException("Cannot create a reference to an object with a NULL id."); + } + } catch (IllegalAccessException e) { + throw new MappingException(e.getMessage(), e); + } catch (InvocationTargetException e) { + throw new MappingException(e.getMessage(), e); + } + + String collection = dbref.collection(); + if ("".equals(collection)) { + collection = targetEntity.getType().getSimpleName().toLowerCase(); + } + + String dbname = dbref.db(); + if ("".equals(dbname)) { + dbname = defaultDatabase; + } + + DB db = mongo.getDB(dbname); + return new DBRef(db, collection, id); + } + + @SuppressWarnings({"unchecked"}) + protected Object getValueInternal(PersistentProperty prop, DBObject dbo, StandardEvaluationContext ctx, Value spelExpr) { + String name = prop.getName(); + Object o; + if (null != spelExpr) { + Expression x = spelExpressionParser.parseExpression(spelExpr.value()); + o = x.getValue(ctx); + } else { + DBObject from = dbo; + if (dbo instanceof DBRef) { + from = ((DBRef) dbo).fetch(); + } + Object dbObj = from.get(name); + if (dbObj instanceof DBObject) { + 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()) { + 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.getMapValueType()), (DBObject) entry.getValue())); + } else { + m.put(entry.getKey(), entry.getValue()); + } + } + return m; + } else if (prop.isArray() && dbObj instanceof BasicDBObject && ((DBObject) dbObj).keySet().size() == 0) { + // It's empty + 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()); + for (int i = 0; i < dbObjList.size(); i++) { + Object dbObjItem = dbObjList.get(i); + if (dbObjItem instanceof DBRef) { + items[i] = read(prop.getComponentType(), ((DBRef) dbObjItem).fetch()); + } else if (dbObjItem instanceof DBObject) { + items[i] = read(prop.getComponentType(), (DBObject) dbObjItem); + } else { + items[i] = dbObjItem; + } + } + return Arrays.asList(items); + } + + Class toType = findTypeToBeUsed((DBObject) dbObj); + + // It's a complex object, have to read it in + if (toType != null) { + dbo.removeField(CUSTOM_TYPE_KEY); + o = read(toType, (DBObject) dbObj); + } else { + o = read(mappingContext.getPersistentEntity(prop.getTypeInformation()), (DBObject) dbObj); + } + } else { + o = dbObj; + } + } + return o; + } + + /** + * Returns the type to be used to convert the DBObject given to. + * + * @param dbObject + * @return + */ + protected Class findTypeToBeUsed(DBObject dbObject) { + Object classToBeUsed = dbObject.get(CUSTOM_TYPE_KEY); + + if (classToBeUsed == null) { + return null; + } + + try { + return Class.forName(classToBeUsed.toString()); + } catch (ClassNotFoundException e) { + throw new MappingException(e.getMessage(), e); + } + } + + public void afterPropertiesSet() throws Exception { + } + + /** + * Simple singleton to convert {@link ObjectId}s to their {@link String} representation. + * + * @author Oliver Gierke + */ + public static enum ObjectIdToStringConverter implements Converter { + INSTANCE; + + public String convert(ObjectId id) { + return id.toString(); + } + } + + /** + * Simple singleton to convert {@link String}s to their {@link ObjectId} representation. + * + * @author Oliver Gierke + */ + public static enum StringToObjectIdConverter implements Converter { + INSTANCE; + + public ObjectId convert(String source) { + return new ObjectId(source); + } + } + + /** + * Simple singleton to convert {@link ObjectId}s to their {@link java.math.BigInteger} representation. + * + * @author Oliver Gierke + */ + public static enum ObjectIdToBigIntegerConverter implements Converter { + INSTANCE; + + public BigInteger convert(ObjectId source) { + return new BigInteger(source.toString(), 16); + } + } + + /** + * Simple singleton to convert {@link BigInteger}s to their {@link ObjectId} representation. + * + * @author Oliver Gierke + */ + public static enum BigIntegerToIdConverter implements Converter { + INSTANCE; + + public ObjectId convert(BigInteger source) { + return new ObjectId(source.toString(16)); + } + } + + protected class PersistentPropertyWrapper { + private final PersistentProperty property; + private final DBObject target; + + public PersistentPropertyWrapper(PersistentProperty property, DBObject target) { + this.property = property; + this.target = target; + } + + public PersistentProperty getProperty() { + return property; + } + + public DBObject getTarget() { + return target; + } + } } 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 7b4795894..4fba0adb0 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 @@ -56,6 +56,9 @@ public class MongoPersistentEntity extends BasicPersistentEntity { * @return */ public String getCollection() { + if (null == collection) { + this.collection = type.getSimpleName().toLowerCase(); + } return collection; } diff --git a/src/docbkx/reference/mapping.xml b/src/docbkx/reference/mapping.xml index 0fe317002..10f73bc1a 100644 --- a/src/docbkx/reference/mapping.xml +++ b/src/docbkx/reference/mapping.xml @@ -23,8 +23,6 @@ http://www.springframework.org/schema/data/mongo http://www.springframework.org/schema/data/mongo/spring-mongo-1.0.xsd http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd"> - -