diff --git a/README.md b/README.md index 76fb0b09d4..479696797b 100644 --- a/README.md +++ b/README.md @@ -1,16 +1,22 @@ -Spring Data - Document +Spring Data MongoDB ====================== The primary goal of the [Spring Data](http://www.springsource.org/spring-data) project is to make it easier to build Spring-powered applications that use new data access technologies such as non-relational databases, map-reduce frameworks, and cloud based data services. -As the name implies, the **Document** modules provides integration with document databases such as [MongoDB](http://www.mongodb.org/) and [CouchDB](http://couchdb.apache.org/). + +The Spring Data MongoDB aims to provide a familiar and consistent Spring-based programming model for for new datastores while retaining store-specific features and capabilities. The Spring Data MongoDB project provides integration with the MongoDB document database. Key functional areas of Spring Data MongoDB are a POJO centric model for interacting with a MongoDB DBCollection and easily writing a Repository style data access layer Getting Help ------------ -At this point your best bet is to look at the Look at the [JavaDocs](http://static.springsource.org/spring-data/data-document/docs/1.0.0.BUILD-SNAPSHOT/spring-data-mongodb/apidocs/) for MongoDB integration and corresponding and source code. For more detailed questions, use the [forum](http://forum.springsource.org/forumdisplay.php?f=80). If you are new to Spring as well as to Spring Data, look for information about [Spring projects](http://www.springsource.org/projects). +For a comprehensive treatmet of all the Spring Data MongoDB features, please refer to the The [User Guide](http://static.springsource.org/spring-data/data-mongodb/docs/current/reference/html/) + +The [JavaDocs](http://static.springsource.org/spring-data/data-mongodb/docs/current/api/) have extensive comments in them as well. + +The home page of [Spring Data MongoDB](http://www.springsource.org/spring-data/mongodb) contains links to articles and other resources. -The [User Guide](http://static.springsource.org/spring-data/data-document/docs/1.0.0.BUILD-SNAPSHOT/reference/html/) (A work in progress). +For more detailed questions, use the [forum](http://forum.springsource.org/forumdisplay.php?f=80). +If you are new to Spring as well as to Spring Data, look for information about [Spring projects](http://www.springsource.org/projects). Quick Start @@ -137,11 +143,6 @@ This will register an object in the container named PersonRepository. You can u } -## CouchDB - -TBD - - Contributing to Spring Data --------------------------- diff --git a/spring-data-mongodb-parent/pom.xml b/spring-data-mongodb-parent/pom.xml index 2386a000fb..88131bbad2 100644 --- a/spring-data-mongodb-parent/pom.xml +++ b/spring-data-mongodb-parent/pom.xml @@ -16,9 +16,8 @@ 1.6.1 1.6.1 3.0.7.RELEASE - 4.0.0.RELEASE - [${org.springframework.version.30}, ${org.springframework.version.40}) - 1.3.2.BUILD-SNAPSHOT + 3.1.2.RELEASE + 1.4.0.BUILD-SNAPSHOT 1.6.11.RELEASE true diff --git a/spring-data-mongodb/pom.xml b/spring-data-mongodb/pom.xml index d136285eff..e5fd0599ff 100644 --- a/spring-data-mongodb/pom.xml +++ b/spring-data-mongodb/pom.xml @@ -181,7 +181,7 @@ com.mysema.maven maven-apt-plugin - 1.0.2 + 1.0.4 generate-test-sources diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/config/AbstractMongoConfiguration.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/config/AbstractMongoConfiguration.java index 4547166f2c..6333d0ef6e 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/config/AbstractMongoConfiguration.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/config/AbstractMongoConfiguration.java @@ -39,7 +39,7 @@ import com.mongodb.Mongo; /** - * Base class for Spring Data Mongo configuration using JavaConfig. + * Base class for Spring Data MongoDB configuration using JavaConfig. * * @author Mark Pollack * @author Oliver Gierke @@ -86,22 +86,26 @@ public MongoTemplate mongoTemplate() throws Exception { @Bean public SimpleMongoDbFactory mongoDbFactory() throws Exception { - UserCredentials creadentials = getUserCredentials(); + UserCredentials credentials = getUserCredentials(); - if (creadentials == null) { + if (credentials == null) { return new SimpleMongoDbFactory(mongo(), getDatabaseName()); } else { - return new SimpleMongoDbFactory(mongo(), getDatabaseName(), creadentials); + return new SimpleMongoDbFactory(mongo(), getDatabaseName(), credentials); } } /** - * Return the base package to scan for mapped {@link Document}s. + * Return the base package to scan for mapped {@link Document}s. Will return the package name of the configuration + * class' (the concrete class, not this one here) by default. So if you have a {@code com.acme.AppConfig} extending + * {@link AbstractMongoConfiguration} the base package will be considered {@code com.acme} unless the method is + * overriden to implement alternate behaviour. * - * @return + * @return the base package to scan for mapped {@link Document} classes or {@literal null} to not enable scanning for + * entities. */ protected String getMappingBasePackage() { - return null; + return getClass().getPackage().getName(); } /** @@ -127,7 +131,7 @@ public MongoMappingContext mongoMappingContext() throws ClassNotFoundException { MongoMappingContext mappingContext = new MongoMappingContext(); mappingContext.setInitialEntitySet(getInitialEntitySet()); mappingContext.setSimpleTypeHolder(customConversions().getSimpleTypeHolder()); - mappingContext.afterPropertiesSet(); + mappingContext.initialize(); return mappingContext; } diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/config/MappingMongoConverterParser.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/config/MappingMongoConverterParser.java index dfb7e97914..8bfd4aaa6c 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/config/MappingMongoConverterParser.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/config/MappingMongoConverterParser.java @@ -48,6 +48,7 @@ import org.springframework.core.type.filter.AssignableTypeFilter; import org.springframework.core.type.filter.TypeFilter; import org.springframework.data.annotation.Persistent; +import org.springframework.data.config.BeanComponentDefinitionBuilder; import org.springframework.data.mongodb.core.convert.CustomConversions; import org.springframework.data.mongodb.core.convert.MappingMongoConverter; import org.springframework.data.mongodb.core.index.MongoPersistentEntityIndexCreator; @@ -166,27 +167,35 @@ private String potentiallyCreateMappingContext(Element element, ParserContext pa BeanDefinition conversionsDefinition) { 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); - } + if (StringUtils.hasText(ctxRef)) { + return ctxRef; + } - if (conversionsDefinition != null) { - AbstractBeanDefinition simpleTypesDefinition = new GenericBeanDefinition(); - simpleTypesDefinition.setFactoryBeanName("customConversions"); - simpleTypesDefinition.setFactoryMethodName("getSimpleTypeHolder"); + BeanComponentDefinitionBuilder componentDefinitionBuilder = new BeanComponentDefinitionBuilder(element, + parserContext); - mappingContextBuilder.addPropertyValue("simpleTypeHolder", simpleTypesDefinition); - } + BeanDefinitionBuilder mappingContextBuilder = BeanDefinitionBuilder + .genericBeanDefinition(MongoMappingContext.class); - parserContext.getRegistry().registerBeanDefinition(MAPPING_CONTEXT, mappingContextBuilder.getBeanDefinition()); - ctxRef = MAPPING_CONTEXT; + Set classesToAdd = getInititalEntityClasses(element, mappingContextBuilder); + if (classesToAdd != null) { + mappingContextBuilder.addPropertyValue("initialEntitySet", classesToAdd); } + if (conversionsDefinition != null) { + AbstractBeanDefinition simpleTypesDefinition = new GenericBeanDefinition(); + simpleTypesDefinition.setFactoryBeanName("customConversions"); + simpleTypesDefinition.setFactoryMethodName("getSimpleTypeHolder"); + + mappingContextBuilder.addPropertyValue("simpleTypeHolder", simpleTypesDefinition); + } + + parserContext.getRegistry().registerBeanDefinition(MAPPING_CONTEXT, mappingContextBuilder.getBeanDefinition()); + ctxRef = MAPPING_CONTEXT; + + parserContext.registerBeanComponent(componentDefinitionBuilder.getComponent(mappingContextBuilder, ctxRef)); + return ctxRef; } diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/config/MongoDbFactoryParser.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/config/MongoDbFactoryParser.java index 4813a4494b..6e65a7c639 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/config/MongoDbFactoryParser.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/config/MongoDbFactoryParser.java @@ -1,5 +1,5 @@ /* - * Copyright 2011 by the original author(s). + * Copyright 2011-2012 by the original author(s). * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,19 +15,19 @@ */ package org.springframework.data.mongodb.config; -import static org.springframework.data.mongodb.config.BeanNames.*; -import static org.springframework.data.mongodb.config.ParsingUtils.*; +import static org.springframework.data.config.ParsingUtils.*; +import static org.springframework.data.mongodb.config.MongoParsingUtils.*; import org.springframework.beans.factory.BeanDefinitionStoreException; import org.springframework.beans.factory.config.BeanDefinition; -import org.springframework.beans.factory.config.RuntimeBeanReference; +import org.springframework.beans.factory.parsing.BeanComponentDefinition; import org.springframework.beans.factory.support.AbstractBeanDefinition; import org.springframework.beans.factory.support.BeanDefinitionBuilder; -import org.springframework.beans.factory.support.BeanDefinitionReaderUtils; import org.springframework.beans.factory.xml.AbstractBeanDefinitionParser; import org.springframework.beans.factory.xml.BeanDefinitionParser; import org.springframework.beans.factory.xml.ParserContext; import org.springframework.data.authentication.UserCredentials; +import org.springframework.data.config.BeanComponentDefinitionBuilder; import org.springframework.data.mongodb.core.MongoFactoryBean; import org.springframework.data.mongodb.core.SimpleMongoDbFactory; import org.springframework.util.StringUtils; @@ -44,19 +44,29 @@ */ public class MongoDbFactoryParser extends AbstractBeanDefinitionParser { + /* + * (non-Javadoc) + * @see org.springframework.beans.factory.xml.AbstractBeanDefinitionParser#resolveId(org.w3c.dom.Element, org.springframework.beans.factory.support.AbstractBeanDefinition, org.springframework.beans.factory.xml.ParserContext) + */ @Override protected String resolveId(Element element, AbstractBeanDefinition definition, ParserContext parserContext) throws BeanDefinitionStoreException { - String id = element.getAttribute("id"); - if (!StringUtils.hasText(id)) { - id = DB_FACTORY; - } - return id; + + String id = super.resolveId(element, definition, parserContext); + return StringUtils.hasText(id) ? id : BeanNames.DB_FACTORY; } + /* + * (non-Javadoc) + * @see org.springframework.beans.factory.xml.AbstractBeanDefinitionParser#parseInternal(org.w3c.dom.Element, org.springframework.beans.factory.xml.ParserContext) + */ @Override protected AbstractBeanDefinition parseInternal(Element element, ParserContext parserContext) { + Object source = parserContext.extractSource(element); + + BeanComponentDefinitionBuilder helper = new BeanComponentDefinitionBuilder(element, parserContext); + String uri = element.getAttribute("uri"); String mongoRef = element.getAttribute("mongo-ref"); String dbname = element.getAttribute("dbname"); @@ -64,12 +74,11 @@ protected AbstractBeanDefinition parseInternal(Element element, ParserContext pa // Common setup BeanDefinitionBuilder dbFactoryBuilder = BeanDefinitionBuilder.genericBeanDefinition(SimpleMongoDbFactory.class); - ParsingUtils.setPropertyValue(element, dbFactoryBuilder, "write-concern", "writeConcern"); + setPropertyValue(dbFactoryBuilder, element, "write-concern", "writeConcern"); if (StringUtils.hasText(uri)) { if (StringUtils.hasText(mongoRef) || StringUtils.hasText(dbname) || userCredentials != null) { - parserContext.getReaderContext().error("Configure either Mongo URI or details individually!", - parserContext.extractSource(element)); + parserContext.getReaderContext().error("Configure either Mongo URI or details individually!", source); } dbFactoryBuilder.addConstructorArgValue(getMongoUri(uri)); @@ -77,19 +86,26 @@ protected AbstractBeanDefinition parseInternal(Element element, ParserContext pa } // Defaulting - mongoRef = StringUtils.hasText(mongoRef) ? mongoRef : registerMongoBeanDefinition(element, parserContext); - dbname = StringUtils.hasText(dbname) ? dbname : "db"; + if (StringUtils.hasText(mongoRef)) { + dbFactoryBuilder.addConstructorArgReference(mongoRef); + } else { + dbFactoryBuilder.addConstructorArgValue(registerMongoBeanDefinition(element, parserContext)); + } - dbFactoryBuilder.addConstructorArgValue(new RuntimeBeanReference(mongoRef)); + dbname = StringUtils.hasText(dbname) ? dbname : "db"; dbFactoryBuilder.addConstructorArgValue(dbname); if (userCredentials != null) { dbFactoryBuilder.addConstructorArgValue(userCredentials); } - ParsingUtils.registerWriteConcernPropertyEditor(parserContext.getRegistry()); + BeanDefinitionBuilder writeConcernPropertyEditorBuilder = getWriteConcernPropertyEditorBuilder(); + + BeanComponentDefinition component = helper.getComponent(writeConcernPropertyEditorBuilder); + parserContext.registerBeanComponent(component); - return getSourceBeanDefinition(dbFactoryBuilder, parserContext, element); + return (AbstractBeanDefinition) helper.getComponentIdButFallback(dbFactoryBuilder, BeanNames.DB_FACTORY) + .getBeanDefinition(); } /** @@ -100,14 +116,13 @@ protected AbstractBeanDefinition parseInternal(Element element, ParserContext pa * @param parserContext must not be {@literal null}. * @return */ - private String registerMongoBeanDefinition(Element element, ParserContext parserContext) { + private BeanDefinition registerMongoBeanDefinition(Element element, ParserContext parserContext) { BeanDefinitionBuilder mongoBuilder = BeanDefinitionBuilder.genericBeanDefinition(MongoFactoryBean.class); - ParsingUtils.setPropertyValue(element, mongoBuilder, "host"); - ParsingUtils.setPropertyValue(element, mongoBuilder, "port"); + setPropertyValue(mongoBuilder, element, "host"); + setPropertyValue(mongoBuilder, element, "port"); - return BeanDefinitionReaderUtils.registerWithGeneratedName(mongoBuilder.getBeanDefinition(), - parserContext.getRegistry()); + return getSourceBeanDefinition(mongoBuilder, parserContext, element); } /** diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/config/MongoNamespaceHandler.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/config/MongoNamespaceHandler.java index 4eb27dee57..a01de78780 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/config/MongoNamespaceHandler.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/config/MongoNamespaceHandler.java @@ -16,7 +16,9 @@ package org.springframework.data.mongodb.config; import org.springframework.beans.factory.xml.NamespaceHandlerSupport; -import org.springframework.data.mongodb.repository.config.MongoRepositoryConfigParser; +import org.springframework.data.mongodb.repository.config.MongoRepositoryConfigurationExtension; +import org.springframework.data.repository.config.RepositoryBeanDefinitionParser; +import org.springframework.data.repository.config.RepositoryConfigurationExtension; /** * {@link org.springframework.beans.factory.xml.NamespaceHandler} for Mongo DB based repositories. @@ -32,7 +34,10 @@ public class MongoNamespaceHandler extends NamespaceHandlerSupport { */ public void init() { - registerBeanDefinitionParser("repositories", new MongoRepositoryConfigParser()); + RepositoryConfigurationExtension extension = new MongoRepositoryConfigurationExtension(); + RepositoryBeanDefinitionParser repositoryBeanDefinitionParser = new RepositoryBeanDefinitionParser(extension); + + registerBeanDefinitionParser("repositories", repositoryBeanDefinitionParser); registerBeanDefinitionParser("mapping-converter", new MappingMongoConverterParser()); registerBeanDefinitionParser("mongo", new MongoParser()); registerBeanDefinitionParser("db-factory", new MongoDbFactoryParser()); diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/config/MongoParser.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/config/MongoParser.java index 8c8be401ee..f03f9c697c 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/config/MongoParser.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/config/MongoParser.java @@ -1,5 +1,5 @@ /* - * Copyright 2011 the original author or authors. + * Copyright 2011-2012 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,20 +13,20 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package org.springframework.data.mongodb.config; import java.util.Map; -import org.springframework.beans.factory.BeanDefinitionStoreException; +import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.beans.factory.config.CustomEditorConfigurer; -import org.springframework.beans.factory.support.AbstractBeanDefinition; +import org.springframework.beans.factory.parsing.BeanComponentDefinition; +import org.springframework.beans.factory.parsing.CompositeComponentDefinition; import org.springframework.beans.factory.support.BeanDefinitionBuilder; -import org.springframework.beans.factory.support.BeanDefinitionReaderUtils; -import org.springframework.beans.factory.support.BeanDefinitionRegistry; import org.springframework.beans.factory.support.ManagedMap; -import org.springframework.beans.factory.xml.AbstractSingleBeanDefinitionParser; +import org.springframework.beans.factory.xml.BeanDefinitionParser; import org.springframework.beans.factory.xml.ParserContext; +import org.springframework.data.config.BeanComponentDefinitionBuilder; +import org.springframework.data.config.ParsingUtils; import org.springframework.data.mongodb.core.MongoFactoryBean; import org.springframework.util.StringUtils; import org.w3c.dom.Element; @@ -35,54 +35,59 @@ * Parser for <mongo;gt; definitions. * * @author Mark Pollack + * @author Oliver Gierke */ -public class MongoParser extends AbstractSingleBeanDefinitionParser { +public class MongoParser implements BeanDefinitionParser { - @Override - protected Class getBeanClass(Element element) { - return MongoFactoryBean.class; - } + /* + * (non-Javadoc) + * @see org.springframework.beans.factory.xml.BeanDefinitionParser#parse(org.w3c.dom.Element, org.springframework.beans.factory.xml.ParserContext) + */ + public BeanDefinition parse(Element element, ParserContext parserContext) { + + Object source = parserContext.extractSource(element); + String id = element.getAttribute("id"); - @Override - protected void doParse(Element element, ParserContext parserContext, BeanDefinitionBuilder builder) { + BeanComponentDefinitionBuilder helper = new BeanComponentDefinitionBuilder(element, parserContext); - ParsingUtils.setPropertyValue(element, builder, "port", "port"); - ParsingUtils.setPropertyValue(element, builder, "host", "host"); - ParsingUtils.setPropertyValue(element, builder, "write-concern", "writeConcern"); + BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(MongoFactoryBean.class); + ParsingUtils.setPropertyValue(builder, element, "port", "port"); + ParsingUtils.setPropertyValue(builder, element, "host", "host"); + ParsingUtils.setPropertyValue(builder, element, "write-concern", "writeConcern"); - ParsingUtils.parseMongoOptions(element, builder); - ParsingUtils.parseReplicaSet(element, builder); + MongoParsingUtils.parseMongoOptions(element, builder); + MongoParsingUtils.parseReplicaSet(element, builder); - registerServerAddressPropertyEditor(parserContext.getRegistry()); - ParsingUtils.registerWriteConcernPropertyEditor(parserContext.getRegistry()); + String defaultedId = StringUtils.hasText(id) ? id : BeanNames.MONGO; + parserContext.pushContainingComponent(new CompositeComponentDefinition("Mongo", source)); + + BeanComponentDefinition mongoComponent = helper.getComponent(builder, defaultedId); + parserContext.registerBeanComponent(mongoComponent); + BeanComponentDefinition serverAddressPropertyEditor = helper.getComponent(registerServerAddressPropertyEditor()); + parserContext.registerBeanComponent(serverAddressPropertyEditor); + BeanComponentDefinition writeConcernPropertyEditor = helper.getComponent(MongoParsingUtils + .getWriteConcernPropertyEditorBuilder()); + parserContext.registerBeanComponent(writeConcernPropertyEditor); + + parserContext.popAndRegisterContainingComponent(); + + return mongoComponent.getBeanDefinition(); } /** * One should only register one bean definition but want to have the convenience of using * AbstractSingleBeanDefinitionParser but have the side effect of registering a 'default' property editor with the * container. - * - * @param parserContext the ParserContext to */ - private void registerServerAddressPropertyEditor(BeanDefinitionRegistry registry) { + private BeanDefinitionBuilder registerServerAddressPropertyEditor() { - BeanDefinitionBuilder customEditorConfigurer = BeanDefinitionBuilder - .genericBeanDefinition(CustomEditorConfigurer.class); Map customEditors = new ManagedMap(); customEditors.put("com.mongodb.ServerAddress[]", "org.springframework.data.mongodb.config.ServerAddressPropertyEditor"); - customEditorConfigurer.addPropertyValue("customEditors", customEditors); - BeanDefinitionReaderUtils.registerWithGeneratedName(customEditorConfigurer.getBeanDefinition(), registry); - } - @Override - protected String resolveId(Element element, AbstractBeanDefinition definition, ParserContext parserContext) - throws BeanDefinitionStoreException { - String name = super.resolveId(element, definition, parserContext); - if (!StringUtils.hasText(name)) { - name = "mongo"; - } - return name; + BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(CustomEditorConfigurer.class); + builder.addPropertyValue("customEditors", customEditors); + return builder; } -} \ No newline at end of file +} diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/config/MongoParsingUtils.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/config/MongoParsingUtils.java new file mode 100644 index 0000000000..6613f7066f --- /dev/null +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/config/MongoParsingUtils.java @@ -0,0 +1,103 @@ +/* + * Copyright 2011-2012 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.data.mongodb.config; + +import static org.springframework.data.config.ParsingUtils.*; + +import java.util.Map; + +import org.springframework.beans.factory.config.BeanDefinition; +import org.springframework.beans.factory.config.CustomEditorConfigurer; +import org.springframework.beans.factory.support.BeanDefinitionBuilder; +import org.springframework.beans.factory.support.ManagedMap; +import org.springframework.beans.factory.xml.BeanDefinitionParser; +import org.springframework.data.mongodb.core.MongoOptionsFactoryBean; +import org.springframework.util.xml.DomUtils; +import org.w3c.dom.Element; + +/** + * Utility methods for {@link BeanDefinitionParser} implementations for MongoDB. + * + * @author Mark Pollack + * @author Oliver Gierke + */ +abstract class MongoParsingUtils { + + private MongoParsingUtils() { + + } + + /** + * Parses the mongo replica-set element. + * + * @param parserContext the parser context + * @param element the mongo element + * @param mongoBuilder the bean definition builder to populate + * @return + */ + static void parseReplicaSet(Element element, BeanDefinitionBuilder mongoBuilder) { + setPropertyValue(mongoBuilder, element, "replica-set", "replicaSetSeeds"); + } + + /** + * Parses the mongo:options sub-element. Populates the given attribute factory with the proper attributes. + * + * @return true if parsing actually occured, false otherwise + */ + static boolean parseMongoOptions(Element element, BeanDefinitionBuilder mongoBuilder) { + Element optionsElement = DomUtils.getChildElementByTagName(element, "options"); + if (optionsElement == null) { + return false; + } + + BeanDefinitionBuilder optionsDefBuilder = BeanDefinitionBuilder + .genericBeanDefinition(MongoOptionsFactoryBean.class); + + setPropertyValue(optionsDefBuilder, optionsElement, "connections-per-host", "connectionsPerHost"); + setPropertyValue(optionsDefBuilder, optionsElement, "threads-allowed-to-block-for-connection-multiplier", + "threadsAllowedToBlockForConnectionMultiplier"); + setPropertyValue(optionsDefBuilder, optionsElement, "max-wait-time", "maxWaitTime"); + setPropertyValue(optionsDefBuilder, optionsElement, "connect-timeout", "connectTimeout"); + setPropertyValue(optionsDefBuilder, optionsElement, "socket-timeout", "socketTimeout"); + setPropertyValue(optionsDefBuilder, optionsElement, "socket-keep-alive", "socketKeepAlive"); + setPropertyValue(optionsDefBuilder, optionsElement, "auto-connect-retry", "autoConnectRetry"); + setPropertyValue(optionsDefBuilder, optionsElement, "max-auto-connect-retry-time", "maxAutoConnectRetryTime"); + setPropertyValue(optionsDefBuilder, optionsElement, "write-number", "writeNumber"); + setPropertyValue(optionsDefBuilder, optionsElement, "write-timeout", "writeTimeout"); + setPropertyValue(optionsDefBuilder, optionsElement, "write-fsync", "writeFsync"); + setPropertyValue(optionsDefBuilder, optionsElement, "slave-ok", "slaveOk"); + + mongoBuilder.addPropertyValue("mongoOptions", optionsDefBuilder.getBeanDefinition()); + return true; + } + + /** + * Returns the {@link BeanDefinitionBuilder} to build a {@link BeanDefinition} for a + * {@link WriteConcernPropertyEditor}. + * + * @return + */ + static BeanDefinitionBuilder getWriteConcernPropertyEditorBuilder() { + + Map> customEditors = new ManagedMap>(); + customEditors.put("com.mongodb.WriteConcern", WriteConcernPropertyEditor.class); + + BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(CustomEditorConfigurer.class); + builder.addPropertyValue("customEditors", customEditors); + + return builder; + } +} diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/config/ParsingUtils.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/config/ParsingUtils.java deleted file mode 100644 index 36a2411d35..0000000000 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/config/ParsingUtils.java +++ /dev/null @@ -1,141 +0,0 @@ -/* - * Copyright (c) 2011 by the original author(s). - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.data.mongodb.config; - -import java.util.Map; - -import org.springframework.beans.factory.config.BeanDefinition; -import org.springframework.beans.factory.config.CustomEditorConfigurer; -import org.springframework.beans.factory.support.AbstractBeanDefinition; -import org.springframework.beans.factory.support.BeanDefinitionBuilder; -import org.springframework.beans.factory.support.BeanDefinitionReaderUtils; -import org.springframework.beans.factory.support.BeanDefinitionRegistry; -import org.springframework.beans.factory.support.ManagedMap; -import org.springframework.beans.factory.xml.ParserContext; -import org.springframework.data.mongodb.core.MongoOptionsFactoryBean; -import org.springframework.util.Assert; -import org.springframework.util.StringUtils; -import org.springframework.util.xml.DomUtils; -import org.w3c.dom.Element; - -abstract class ParsingUtils { - - /** - * Parses the mongo replica-set element. - * - * @param parserContext the parser context - * @param element the mongo element - * @param mongoBuilder the bean definition builder to populate - * @return true if parsing actually occured, false otherwise - */ - static boolean parseReplicaSet(Element element, BeanDefinitionBuilder mongoBuilder) { - - String replicaSetString = element.getAttribute("replica-set"); - if (StringUtils.hasText(replicaSetString)) { - mongoBuilder.addPropertyValue("replicaSetSeeds", replicaSetString); - } - return true; - - } - - /** - * Parses the mongo:options sub-element. Populates the given attribute factory with the proper attributes. - * - * @return true if parsing actually occured, false otherwise - */ - static boolean parseMongoOptions(Element element, BeanDefinitionBuilder mongoBuilder) { - Element optionsElement = DomUtils.getChildElementByTagName(element, "options"); - if (optionsElement == null) { - return false; - } - - BeanDefinitionBuilder optionsDefBuilder = BeanDefinitionBuilder - .genericBeanDefinition(MongoOptionsFactoryBean.class); - - setPropertyValue(optionsElement, optionsDefBuilder, "connections-per-host", "connectionsPerHost"); - setPropertyValue(optionsElement, optionsDefBuilder, "threads-allowed-to-block-for-connection-multiplier", - "threadsAllowedToBlockForConnectionMultiplier"); - setPropertyValue(optionsElement, optionsDefBuilder, "max-wait-time", "maxWaitTime"); - setPropertyValue(optionsElement, optionsDefBuilder, "connect-timeout", "connectTimeout"); - setPropertyValue(optionsElement, optionsDefBuilder, "socket-timeout", "socketTimeout"); - setPropertyValue(optionsElement, optionsDefBuilder, "socket-keep-alive", "socketKeepAlive"); - setPropertyValue(optionsElement, optionsDefBuilder, "auto-connect-retry", "autoConnectRetry"); - setPropertyValue(optionsElement, optionsDefBuilder, "max-auto-connect-retry-time", "maxAutoConnectRetryTime"); - setPropertyValue(optionsElement, optionsDefBuilder, "write-number", "writeNumber"); - setPropertyValue(optionsElement, optionsDefBuilder, "write-timeout", "writeTimeout"); - setPropertyValue(optionsElement, optionsDefBuilder, "write-fsync", "writeFsync"); - setPropertyValue(optionsElement, optionsDefBuilder, "slave-ok", "slaveOk"); - - mongoBuilder.addPropertyValue("mongoOptions", optionsDefBuilder.getBeanDefinition()); - return true; - } - - static void setPropertyValue(Element element, BeanDefinitionBuilder builder, String attrName, String propertyName) { - String attr = element.getAttribute(attrName); - if (StringUtils.hasText(attr)) { - builder.addPropertyValue(propertyName, attr); - } - } - - /** - * Sets the property with the given attribute name on the given {@link BeanDefinitionBuilder} to the value of the - * attribute with the given name. - * - * @param element must not be {@literal null}. - * @param builder must not be {@literal null}. - * @param attrName must not be {@literal null} or empty. - */ - static void setPropertyValue(Element element, BeanDefinitionBuilder builder, String attrName) { - String attr = element.getAttribute(attrName); - if (StringUtils.hasText(attr)) { - builder.addPropertyValue(attrName, attr); - } - } - - /** - * Returns the {@link BeanDefinition} built by the given {@link BeanDefinitionBuilder} enriched with source - * information derived from the given {@link Element}. - * - * @param builder must not be {@literal null}. - * @param context must not be {@literal null}. - * @param element must not be {@literal null}. - * @return - */ - static AbstractBeanDefinition getSourceBeanDefinition(BeanDefinitionBuilder builder, ParserContext context, - Element element) { - AbstractBeanDefinition definition = builder.getBeanDefinition(); - definition.setSource(context.extractSource(element)); - return definition; - } - - /** - * Registers a {@link WriteConcernPropertyEditor} in the given {@link BeanDefinitionRegistry}. - * - * @param registry must not be {@literal null}. - */ - static void registerWriteConcernPropertyEditor(BeanDefinitionRegistry registry) { - - Assert.notNull(registry); - - BeanDefinitionBuilder customEditorConfigurer = BeanDefinitionBuilder - .genericBeanDefinition(CustomEditorConfigurer.class); - Map> customEditors = new ManagedMap>(); - customEditors.put("com.mongodb.WriteConcern", WriteConcernPropertyEditor.class); - customEditorConfigurer.addPropertyValue("customEditors", customEditors); - BeanDefinitionReaderUtils.registerWithGeneratedName(customEditorConfigurer.getBeanDefinition(), registry); - } -} diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/MongoOperations.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/MongoOperations.java index 0b2dea58e4..3cf7304ddb 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/MongoOperations.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/MongoOperations.java @@ -657,6 +657,8 @@ T findAndModify(Query query, Update update, FindAndModifyOptions options, Cl * @return the WriteResult which lets you access the results of the previous write. */ WriteResult updateFirst(Query query, Update update, Class entityClass); + + WriteResult updateFirst(Query query, Object object, Class entityClass); /** * Updates the first object that is found in the specified collection that matches the query document criteria with diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/MongoTemplate.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/MongoTemplate.java index 7ff775bb44..84eec1615b 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/MongoTemplate.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/MongoTemplate.java @@ -15,10 +15,10 @@ */ package org.springframework.data.mongodb.core; +import static org.springframework.data.mongodb.core.SerializationUtils.*; import static org.springframework.data.mongodb.core.query.Criteria.*; import java.io.IOException; -import java.lang.reflect.InvocationTargetException; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; @@ -44,6 +44,7 @@ import org.springframework.dao.DataAccessException; import org.springframework.dao.DataIntegrityViolationException; import org.springframework.dao.InvalidDataAccessApiUsageException; +import org.springframework.dao.OptimisticLockingFailureException; import org.springframework.data.authentication.UserCredentials; import org.springframework.data.convert.EntityReader; import org.springframework.data.mapping.PersistentEntity; @@ -106,6 +107,7 @@ * @author Graeme Rocher * @author Mark Pollack * @author Oliver Gierke + * @author Amol Nayak */ public class MongoTemplate implements MongoOperations, ApplicationContextAware { @@ -206,7 +208,6 @@ public MongoTemplate(MongoDbFactory mongoDbFactory, MongoConverter mongoConverte ((ApplicationEventPublisherAware) mappingContext).setApplicationEventPublisher(eventPublisher); } } - } /** @@ -331,11 +332,12 @@ protected void executeQuery(Query query, String collectionName, DocumentCallback Assert.notNull(query); DBObject queryObject = query.getQueryObject(); + DBObject sortObject = query.getSortObject(); DBObject fieldsObject = query.getFieldsObject(); if (LOGGER.isDebugEnabled()) { - LOGGER.debug("find using query: " + queryObject + " fields: " + fieldsObject + " in collection: " - + collectionName); + LOGGER.debug(String.format("Executing query: %s sort: %s fields: %s in collection: $s", + serializeToJsonSafely(queryObject), sortObject, fieldsObject, collectionName)); } this.executeQueryInternal(new FindCallback(queryObject, fieldsObject), preparer, dch, collectionName); @@ -702,7 +704,26 @@ public void save(Object objectToSave) { } public void save(Object objectToSave, String collectionName) { - doSave(collectionName, objectToSave, this.mongoConverter); + MongoPersistentEntity mongoPersistentEntity = getPersistentEntity(objectToSave.getClass()); + if(mongoPersistentEntity.hasVersion()){ + BeanWrapper, Object> beanWrapper = BeanWrapper.create(objectToSave, null); + Object id = beanWrapper.getProperty(mongoPersistentEntity.getIdProperty()); + if(id == null){ + beanWrapper.setProperty(mongoPersistentEntity.getVersionProperty(), 0); + doSave(collectionName, objectToSave, this.mongoConverter); + } else { + updateFirst( getUpdateVersionQuery(id, + beanWrapper.getProperty(mongoPersistentEntity.getVersionProperty()),mongoPersistentEntity), + objectToSave, objectToSave.getClass()); + } + } else { + doSave(collectionName, objectToSave, this.mongoConverter); + } + } + + private Query getUpdateVersionQuery(Object id, Object version,MongoPersistentEntity mongoPersistentEntity) { + return new Query( Criteria.where(mongoPersistentEntity.getIdProperty().getName()).is(id) + .and(mongoPersistentEntity.getVersionProperty().getName()).is(version)); } protected void doSave(String collectionName, T objectToSave, MongoWriter writer) { @@ -730,11 +751,13 @@ public Object doInCollection(DBCollection collection) throws MongoException, Dat MongoAction mongoAction = new MongoAction(writeConcern, MongoActionOperation.INSERT, collectionName, entityClass, dbDoc, null); WriteConcern writeConcernToUse = prepareWriteConcern(mongoAction); + WriteResult wr; if (writeConcernToUse == null) { - collection.insert(dbDoc); + wr = collection.insert(dbDoc); } else { - collection.insert(dbDoc, writeConcernToUse); + wr = collection.insert(dbDoc, writeConcernToUse); } + handleAnyWriteResultErrors(wr, dbDoc, "insert"); return dbDoc.get(ID); } }); @@ -753,11 +776,13 @@ public Void doInCollection(DBCollection collection) throws MongoException, DataA MongoAction mongoAction = new MongoAction(writeConcern, MongoActionOperation.INSERT_LIST, collectionName, null, null, null); WriteConcern writeConcernToUse = prepareWriteConcern(mongoAction); + WriteResult wr; if (writeConcernToUse == null) { - collection.insert(dbDocList); + wr = collection.insert(dbDocList); } else { - collection.insert(dbDocList.toArray((DBObject[]) new BasicDBObject[dbDocList.size()]), writeConcernToUse); + wr = collection.insert(dbDocList.toArray((DBObject[]) new BasicDBObject[dbDocList.size()]), writeConcernToUse); } + handleAnyWriteResultErrors(wr, null, "insert_list"); return null; } }); @@ -784,11 +809,13 @@ public Object doInCollection(DBCollection collection) throws MongoException, Dat MongoAction mongoAction = new MongoAction(writeConcern, MongoActionOperation.SAVE, collectionName, entityClass, dbDoc, null); WriteConcern writeConcernToUse = prepareWriteConcern(mongoAction); + WriteResult wr; if (writeConcernToUse == null) { - collection.save(dbDoc); + wr = collection.save(dbDoc); } else { - collection.save(dbDoc, writeConcernToUse); + wr = collection.save(dbDoc, writeConcernToUse); } + handleAnyWriteResultErrors(wr, dbDoc, "save"); return dbDoc.get(ID); } }); @@ -809,6 +836,18 @@ public WriteResult updateFirst(Query query, Update update, Class entityClass) public WriteResult updateFirst(final Query query, final Update update, final String collectionName) { return doUpdate(collectionName, query, update, null, false, false); } + + public WriteResult updateFirst(final Query query, final Object object, + Class entityClass) { + + BasicDBObject dbObject = new BasicDBObject(); + this.mongoConverter.write(object, dbObject); + + return doUpdate(determineCollectionName(entityClass), query, + Update.fromDBObject(dbObject, ID, + getPersistentEntity(entityClass).getVersionProperty() + .getName()), entityClass, false, false); + } public WriteResult updateMulti(Query query, Update update, Class entityClass) { return doUpdate(determineCollectionName(entityClass), query, update, entityClass, false, true); @@ -828,9 +867,14 @@ public WriteResult doInCollection(DBCollection collection) throws MongoException DBObject queryObj = query == null ? new BasicDBObject() : mapper.getMappedObject(query.getQueryObject(), entity); + + if(null != update && null != entity && entity.hasVersion()) { + update.inc(entity.getVersionProperty().getName(), 1); + } + DBObject updateObj = update == null ? new BasicDBObject() : mapper.getMappedObject(update.getUpdateObject(), entity); - + if (LOGGER.isDebugEnabled()) { LOGGER.debug("calling update using query: " + queryObj + " and update: " + updateObj + " in collection: " + collectionName); @@ -845,6 +889,11 @@ public WriteResult doInCollection(DBCollection collection) throws MongoException } else { wr = collection.update(queryObj, updateObj, upsert, multi, writeConcernToUse); } + if(null != entity && entity.hasVersion() && !multi){ + if(wr.getN() == 0){ + throw new OptimisticLockingFailureException("Optimistic lock exception on saveing entity: "+updateObj.toMap().toString()); + } + } handleAnyWriteResultErrors(wr, queryObj, "update with '" + updateObj + "'"); return wr; } @@ -873,7 +922,7 @@ public void remove(Object object, String collection) { /** * Returns a {@link Query} for the given entity by its id. - * + * * @param object must not be {@literal null}. * @return */ @@ -991,26 +1040,15 @@ public MapReduceResults mapReduce(Query query, String inputCollectionName LOGGER.debug("Executing MapReduce on collection [" + command.getInput() + "], mapFunction [" + mapFunc + "], reduceFunction [" + reduceFunc + "]"); } - CommandResult commandResult = null; - try { - if (command.getOutputType() == MapReduceCommand.OutputType.INLINE) { - commandResult = executeCommand(commandObject, getDb().getOptions()); - } else { - commandResult = executeCommand(commandObject); - } - commandResult.throwOnError(); - } catch (RuntimeException ex) { - this.potentiallyConvertRuntimeException(ex); - } - String error = commandResult.getErrorMessage(); - if (error != null) { - throw new InvalidDataAccessApiUsageException("Command execution failed: Error [" + error + "], Command = " - + commandObject); - } + + CommandResult commandResult = command.getOutputType() == MapReduceCommand.OutputType.INLINE ? executeCommand( + commandObject, getDb().getOptions()) : executeCommand(commandObject); + handleCommandError(commandResult, commandObject); if (LOGGER.isDebugEnabled()) { - LOGGER.debug("MapReduce command result = [" + commandResult + "]"); + LOGGER.debug(String.format("MapReduce command result = [%s]", serializeToJsonSafely(commandObject))); } + MapReduceOutput mapReduceOutput = new MapReduceOutput(inputCollection, commandObject, commandResult); List mappedResults = new ArrayList(); DbObjectCallback callback = new ReadDbObjectCallback(mongoConverter, entityClass); @@ -1035,7 +1073,7 @@ public GroupByResults group(Criteria criteria, String inputCollectionName if (criteria == null) { dbo.put("cond", null); } else { - dbo.put("cond", criteria.getCriteriaObject()); + dbo.put("cond", mapper.getMappedObject(criteria.getCriteriaObject(), null)); } // If initial document was a JavaScript string, potentially loaded by Spring's Resource abstraction, load it and // convert to DBObject @@ -1061,21 +1099,12 @@ public GroupByResults group(Criteria criteria, String inputCollectionName DBObject commandObject = new BasicDBObject("group", dbo); if (LOGGER.isDebugEnabled()) { - LOGGER.debug("Executing Group with DBObject [" + commandObject.toString() + "]"); - } - CommandResult commandResult = null; - try { - commandResult = executeCommand(commandObject, getDb().getOptions()); - commandResult.throwOnError(); - } catch (RuntimeException ex) { - this.potentiallyConvertRuntimeException(ex); - } - String error = commandResult.getErrorMessage(); - if (error != null) { - throw new InvalidDataAccessApiUsageException("Command execution failed: Error [" + error + "], Command = " - + commandObject); + LOGGER.debug(String.format("Executing Group with DBObject [%s]", serializeToJsonSafely(commandObject))); } + CommandResult commandResult = executeCommand(commandObject, getDb().getOptions()); + handleCommandError(commandResult, commandObject); + if (LOGGER.isDebugEnabled()) { LOGGER.debug("Group command result = [" + commandResult + "]"); } @@ -1182,7 +1211,7 @@ protected void maybeEmitEvent(MongoMappingEvent event) { /** * Create the specified collection using the provided options - * + * * @param collectionName * @param collectionOptions * @return the collection that was created @@ -1204,7 +1233,7 @@ public DBCollection doInDB(DB db) throws MongoException, DataAccessException { * Map the results of an ad-hoc query on the default MongoDB collection to an object using the template's converter *

* The query document is specified as a standard DBObject and so is the fields specification. - * + * * @param collectionName name of the collection to retrieve the objects from * @param query the query document that specifies the criteria used to find a record * @param fields the document that specifies the fields to be returned @@ -1229,7 +1258,7 @@ protected T doFindOne(String collectionName, DBObject query, DBObject fields * The query document is specified as a standard DBObject and so is the fields specification. *

* Can be overridden by subclasses. - * + * * @param collectionName name of the collection to retrieve the objects from * @param query the query document that specifies the criteria used to find a record * @param fields the document that specifies the fields to be returned @@ -1246,11 +1275,14 @@ protected List doFind(String collectionName, DBObject query, DBObject fie protected List doFind(String collectionName, DBObject query, DBObject fields, Class entityClass, CursorPreparer preparer, DbObjectCallback objectCallback) { + MongoPersistentEntity entity = mappingContext.getPersistentEntity(entityClass); + if (LOGGER.isDebugEnabled()) { - LOGGER.debug("find using query: " + query + " fields: " + fields + " for class: " + entityClass - + " in collection: " + collectionName); + LOGGER.debug(String.format("find using query: %s fields: %s for class: %s in collection: %s", + serializeToJsonSafely(query), fields, entityClass, collectionName)); } + return executeFindMultiInternal(new FindCallback(mapper.getMappedObject(query, entity), fields), preparer, objectCallback, collectionName); } @@ -1259,7 +1291,7 @@ protected List doFind(String collectionName, DBObject query, DBObject * Map the results of an ad-hoc query on the default MongoDB collection to a List using the template's converter. *

* The query document is specified as a standard DBObject and so is the fields specification. - * + * * @param collectionName name of the collection to retrieve the objects from * @param query the query document that specifies the criteria used to find a record * @param fields the document that specifies the fields to be returned @@ -1298,7 +1330,7 @@ protected DBObject convertToDbObject(CollectionOptions collectionOptions) { * The first document that matches the query is returned and also removed from the collection in the database. *

* The query document is specified as a standard DBObject and so is the fields specification. - * + * * @param collectionName name of the collection to retrieve the objects from * @param query the query document that specifies the criteria used to find a record * @param entityClass the parameterized type of the returned list. @@ -1345,7 +1377,7 @@ protected T doFindAndModify(String collectionName, DBObject query, DBObject /** * Populates the id property of the saved object, if it's not set already. - * + * * @param savedObject * @param id */ @@ -1364,21 +1396,13 @@ protected void populateIdIfNecessary(Object savedObject, Object id) { ConversionService conversionService = mongoConverter.getConversionService(); BeanWrapper, Object> wrapper = BeanWrapper.create(savedObject, conversionService); - try { + Object idValue = wrapper.getProperty(idProp, idProp.getType(), true); - Object idValue = wrapper.getProperty(idProp); - - if (idValue != null) { - return; - } - - wrapper.setProperty(idProp, id); - - } catch (IllegalAccessException e) { - throw new MappingException(e.getMessage(), e); - } catch (InvocationTargetException e) { - throw new MappingException(e.getMessage(), e); + if (idValue != null) { + return; } + + wrapper.setProperty(idProp, id); } private DBCollection getAndPrepareCollection(DB db, String collectionName) { @@ -1398,7 +1422,7 @@ private DBCollection getAndPrepareCollection(DB db, String collectionName) { *

  • Execute the given {@link ConnectionCallback} for a {@link DBObject}.
  • *
  • Apply the given {@link DbObjectCallback} to each of the {@link DBObject}s to obtain the result.
  • *
      - * + * * @param * @param collectionCallback the callback to retrieve the {@link DBObject} with * @param objectCallback the {@link DbObjectCallback} to transform {@link DBObject}s into the actual domain type @@ -1521,9 +1545,16 @@ protected void handleAnyWriteResultErrors(WriteResult wr, DBObject query, String String error = wr.getError(); if (error != null) { - - String message = String.format("Execution of %s%s failed: %s", operation, query == null ? "" : "' using '" - + query.toString() + "' query", error); + String message; + if (operation.equals("insert") || operation.equals("save")) { + // assuming the insert operations will begin with insert string + message = String.format("Insert/Save for %s failed: %s", query, error); + } else if (operation.equals("insert_list")) { + message = String.format("Insert list failed: %s", error); + } else { + message = String.format("Execution of %s%s failed: %s", operation, + query == null ? "" : "' using '" + query.toString() + "' query", error); + } if (WriteResultChecking.EXCEPTION == this.writeResultChecking) { throw new DataIntegrityViolationException(message); @@ -1546,6 +1577,27 @@ private RuntimeException potentiallyConvertRuntimeException(RuntimeException ex) return resolved == null ? ex : resolved; } + /** + * Inspects the given {@link CommandResult} for erros and potentially throws an + * {@link InvalidDataAccessApiUsageException} for that error. + * + * @param result must not be {@literal null}. + * @param source must not be {@literal null}. + */ + private void handleCommandError(CommandResult result, DBObject source) { + + try { + result.throwOnError(); + } catch (MongoException ex) { + + String error = result.getErrorMessage(); + error = error == null ? "NO MESSAGE" : error; + + throw new InvalidDataAccessApiUsageException("Command execution failed: Error [" + error + "], Command = " + + source, ex); + } + } + private static final MongoConverter getDefaultMongoConverter(MongoDbFactory factory) { MappingMongoConverter converter = new MappingMongoConverter(factory, new MongoMappingContext()); converter.afterPropertiesSet(); diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/CustomConversions.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/CustomConversions.java index ada9514750..40354e1ecd 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/CustomConversions.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/CustomConversions.java @@ -223,7 +223,7 @@ public Class getCustomWriteTarget(Class source) { /** * Returns the target type we can write an onject of the given source type to. The returned type might be a subclass - * oth the given expected type though. If {@code expexctedTargetType} is {@literal null} we will simply return the + * oth the given expected type though. If {@code expectedTargetType} is {@literal null} we will simply return the * first target type matching or {@literal null} if no conversion can be found. * * @param source must not be {@literal null} diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/MappingMongoConverter.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/MappingMongoConverter.java index e5d90c5a39..359c4cd5d2 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/MappingMongoConverter.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/MappingMongoConverter.java @@ -15,7 +15,6 @@ */ package org.springframework.data.mongodb.core.convert; -import java.lang.reflect.InvocationTargetException; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; @@ -253,13 +252,9 @@ public void doWithPersistentProperty(MongoPersistentProperty prop) { public void doWithAssociation(Association association) { MongoPersistentProperty inverseProp = association.getInverse(); Object obj = getValueInternal(inverseProp, dbo, evaluator, result); - try { - wrapper.setProperty(inverseProp, obj); - } catch (IllegalAccessException e) { - throw new MappingException(e.getMessage(), e); - } catch (InvocationTargetException e) { - throw new MappingException(e.getMessage(), e); - } + + wrapper.setProperty(inverseProp, obj); + } }); @@ -676,6 +671,10 @@ protected DBRef createDBRef(Object target, org.springframework.data.mongodb.core Assert.notNull(target); + if (target instanceof DBRef) { + return (DBRef) target; + } + MongoPersistentEntity targetEntity = mappingContext.getPersistentEntity(target.getClass()); if (null == targetEntity) { @@ -713,18 +712,19 @@ protected Object getValueInternal(MongoPersistentProperty prop, DBObject dbo, Sp * * @param targetType must not be {@literal null}. * @param sourceValue must not be {@literal null}. - * @return the converted {@link Collections}, will never be {@literal null}. + * @return the converted {@link Collection} or array, will never be {@literal null}. */ @SuppressWarnings("unchecked") - private Collection readCollectionOrArray(TypeInformation targetType, BasicDBList sourceValue, Object parent) { + private Object readCollectionOrArray(TypeInformation targetType, BasicDBList sourceValue, Object parent) { Assert.notNull(targetType); + Class collectionType = targetType.getType(); + if (sourceValue.isEmpty()) { - return new HashSet(); + return getPotentiallyConvertedSimpleRead(new HashSet(), collectionType); } - Class collectionType = targetType.getType(); collectionType = Collection.class.isAssignableFrom(collectionType) ? collectionType : List.class; Collection items = targetType.getType().isArray() ? new ArrayList() : CollectionFactory @@ -746,7 +746,7 @@ private Collection readCollectionOrArray(TypeInformation targetType, Basic } } - return items; + return getPotentiallyConvertedSimpleRead(items, targetType.getType()); } /** @@ -948,7 +948,7 @@ public T getPropertyValue(MongoPersistentProperty property) { } else if (value instanceof DBRef) { return (T) (rawType.equals(DBRef.class) ? value : read(type, ((DBRef) value).fetch(), parent)); } else if (value instanceof BasicDBList) { - return (T) getPotentiallyConvertedSimpleRead(readCollectionOrArray(type, (BasicDBList) value, parent), rawType); + return (T) readCollectionOrArray(type, (BasicDBList) value, parent); } else if (value instanceof DBObject) { return (T) read(type, (DBObject) value, parent); } else { diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/QueryMapper.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/QueryMapper.java index 4c6aef514b..279b93bd36 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/QueryMapper.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/QueryMapper.java @@ -1,11 +1,11 @@ /* - * Copyright (c) 2011 by the original author(s). + * Copyright 2011-2012 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, @@ -17,14 +17,16 @@ import java.util.ArrayList; import java.util.Arrays; -import java.util.Iterator; import java.util.List; -import org.bson.types.BasicBSONList; import org.bson.types.ObjectId; import org.springframework.core.convert.ConversionException; import org.springframework.core.convert.ConversionService; import org.springframework.data.mapping.PersistentEntity; +import org.springframework.data.mapping.PropertyPath; +import org.springframework.data.mapping.PropertyReferenceException; +import org.springframework.data.mapping.context.MappingContext; +import org.springframework.data.mapping.context.PersistentPropertyPath; import org.springframework.data.mongodb.core.mapping.MongoPersistentEntity; import org.springframework.data.mongodb.core.mapping.MongoPersistentProperty; import org.springframework.util.Assert; @@ -32,11 +34,12 @@ import com.mongodb.BasicDBList; import com.mongodb.BasicDBObject; import com.mongodb.DBObject; +import com.mongodb.DBRef; /** * A helper class to encapsulate any modifications of a Query object before it gets submitted to the database. * - * @author Jon Brisbin + * @author Jon Brisbin * @author Oliver Gierke */ public class QueryMapper { @@ -46,6 +49,7 @@ public class QueryMapper { private final ConversionService conversionService; private final MongoConverter converter; + private final MappingContext, MongoPersistentProperty> mappingContext; /** * Creates a new {@link QueryMapper} with the given {@link MongoConverter}. @@ -53,9 +57,12 @@ public class QueryMapper { * @param converter must not be {@literal null}. */ public QueryMapper(MongoConverter converter) { + Assert.notNull(converter); + this.conversionService = converter.getConversionService(); this.converter = converter; + this.mappingContext = converter.getMappingContext(); } /** @@ -68,47 +75,151 @@ public QueryMapper(MongoConverter converter) { */ public DBObject getMappedObject(DBObject query, MongoPersistentEntity entity) { - DBObject newDbo = new BasicDBObject(); + if (Keyword.isKeyword(query)) { + return getMappedKeyword(new Keyword(query), entity); + } + + DBObject result = new BasicDBObject(); for (String key : query.keySet()) { - String newKey = key; + MongoPersistentProperty targetProperty = getTargetProperty(key, entity); + String newKey = determineKey(key, entity); Object value = query.get(key); - if (isIdKey(key, entity)) { - if (value instanceof DBObject) { - DBObject valueDbo = (DBObject) value; - if (valueDbo.containsField("$in") || valueDbo.containsField("$nin")) { - String inKey = valueDbo.containsField("$in") ? "$in" : "$nin"; - List ids = new ArrayList(); - for (Object id : (Iterable) valueDbo.get(inKey)) { - ids.add(convertId(id)); - } - valueDbo.put(inKey, ids.toArray(new Object[ids.size()])); - } else { - value = getMappedObject((DBObject) value, null); + result.put(newKey, getMappedValue(value, targetProperty, newKey)); + } + + return result; + } + + /** + * Returns the given {@link DBObject} representing a keyword by mapping the keyword's value. + * + * @param query the {@link DBObject} representing a keyword (e.g. {@code $ne : … } ) + * @param entity + * @return + */ + private DBObject getMappedKeyword(Keyword query, MongoPersistentEntity entity) { + + // $or/$nor + if (query.key.matches(N_OR_PATTERN)) { + + Iterable conditions = (Iterable) query.value; + BasicDBList newConditions = new BasicDBList(); + + for (Object condition : conditions) { + newConditions.add(getMappedObject((DBObject) condition, entity)); + } + + return new BasicDBObject(query.key, newConditions); + } + + return new BasicDBObject(query.key, convertSimpleOrDBObject(query.value, entity)); + } + + /** + * Returns the mapped keyword considered defining a criteria for the given property. + * + * @param keyword + * @param property + * @return + */ + public DBObject getMappedKeyword(Keyword keyword, MongoPersistentProperty property) { + + if (property.isAssociation()) { + convertAssociation(keyword.value, property); + } + + return new BasicDBObject(keyword.key, getMappedValue(keyword.value, property, keyword.key)); + } + + /** + * Returns the mapped value for the given source object assuming it's a value for the given + * {@link MongoPersistentProperty}. + * + * @param source the source object to be mapped + * @param property the property the value is a value for + * @param newKey the key the value will be bound to eventually + * @return + */ + private Object getMappedValue(Object source, MongoPersistentProperty property, String newKey) { + + if (property == null) { + return convertSimpleOrDBObject(source, null); + } + + if (property.isIdProperty() || "_id".equals(newKey)) { + + if (source instanceof DBObject) { + DBObject valueDbo = (DBObject) source; + if (valueDbo.containsField("$in") || valueDbo.containsField("$nin")) { + String inKey = valueDbo.containsField("$in") ? "$in" : "$nin"; + List ids = new ArrayList(); + for (Object id : (Iterable) valueDbo.get(inKey)) { + ids.add(convertId(id)); } + valueDbo.put(inKey, ids.toArray(new Object[ids.size()])); + } else if (valueDbo.containsField("$ne")) { + valueDbo.put("$ne", convertId(valueDbo.get("$ne"))); } else { - value = convertId(value); + return getMappedObject((DBObject) source, null); } - newKey = "_id"; - } else if (key.matches(N_OR_PATTERN)) { - // $or/$nor - Iterable conditions = (Iterable) value; - BasicBSONList newConditions = new BasicBSONList(); - Iterator iter = conditions.iterator(); - while (iter.hasNext()) { - newConditions.add(getMappedObject((DBObject) iter.next(), null)); - } - value = newConditions; - } else if (key.equals("$ne")) { - value = convertId(value); + + return valueDbo; + + } else { + return convertId(source); } + } + + if (property.isAssociation()) { + return Keyword.isKeyword(source) ? getMappedKeyword(new Keyword(source), property) : convertAssociation(source, + property); + } + + return convertSimpleOrDBObject(source, mappingContext.getPersistentEntity(property)); + } - newDbo.put(newKey, convertSimpleOrDBObject(value, null)); + private MongoPersistentProperty getTargetProperty(String key, MongoPersistentEntity entity) { + + if (isIdKey(key, entity)) { + return entity.getIdProperty(); + } + + PersistentPropertyPath path = getPath(key, entity); + return path == null ? null : path.getLeafProperty(); + } + + private PersistentPropertyPath getPath(String key, MongoPersistentEntity entity) { + + if (entity == null) { + return null; + } + + try { + PropertyPath path = PropertyPath.from(key, entity.getTypeInformation()); + return mappingContext.getPersistentPropertyPath(path); + } catch (PropertyReferenceException e) { + return null; + } + } + + /** + * Returns the translated key assuming the given one is a propert (path) reference. + * + * @param key the source key + * @param entity the base entity + * @return the translated key + */ + private String determineKey(String key, MongoPersistentEntity entity) { + + if (entity == null && DEFAULT_ID_NAMES.contains(key)) { + return "_id"; } - return newDbo; + PersistentPropertyPath path = getPath(key, entity); + return path == null ? key : path.toDotPath(MongoPersistentProperty.PropertyToFieldNameConverter.INSTANCE); } /** @@ -131,6 +242,30 @@ private Object convertSimpleOrDBObject(Object source, MongoPersistentEntity e return converter.convertToMongoType(source); } + /** + * Converts the given source assuming it's actually an association to anoter object. + * + * @param source + * @param property + * @return + */ + private Object convertAssociation(Object source, MongoPersistentProperty property) { + + if (property == null || !property.isAssociation()) { + return source; + } + + if (source instanceof Iterable) { + BasicDBList result = new BasicDBList(); + for (Object element : (Iterable) source) { + result.add(element instanceof DBRef ? element : converter.toDBRef(element, property)); + } + return result; + } + + return source instanceof DBRef ? source : converter.toDBRef(source, property); + } + /** * Returns whether the given key will be considered an id key. * @@ -169,4 +304,44 @@ public Object convertId(Object id) { return converter.convertToMongoType(id); } + + /** + * Value object to capture a query keyword representation. + * + * @author Oliver Gierke + */ + private static class Keyword { + + String key; + Object value; + + Keyword(Object source) { + + Assert.isInstanceOf(DBObject.class, source); + + DBObject value = (DBObject) source; + + Assert.isTrue(value.keySet().size() == 1, "Keyword must have a single key only!"); + + this.key = value.keySet().iterator().next(); + this.value = value.get(key); + } + + /** + * Returns whether the given value actually represents a keyword. If this returns {@literal true} it's safe to call + * the constructor. + * + * @param value + * @return + */ + static boolean isKeyword(Object value) { + + if (!(value instanceof DBObject)) { + return false; + } + + DBObject dbObject = (DBObject) value; + return dbObject.keySet().size() == 1 && dbObject.keySet().iterator().next().startsWith("$"); + } + } } diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/index/CompoundIndex.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/index/CompoundIndex.java index 19fdbd1716..b8acbf5c7d 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/index/CompoundIndex.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/index/CompoundIndex.java @@ -1,11 +1,11 @@ /* - * Copyright (c) 2011 by the original author(s). + * Copyright 2011-2012 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, @@ -13,9 +13,9 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package org.springframework.data.mongodb.core.index; +import java.lang.annotation.Documented; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; @@ -24,14 +24,29 @@ /** * Mark a class to use compound indexes. * - * @author Jon Brisbin + * @author Jon Brisbin + * @author Oliver Gierke */ @Target({ ElementType.TYPE }) +@Documented @Retention(RetentionPolicy.RUNTIME) public @interface CompoundIndex { + /** + * The actual index definition in JSON format. The keys of the JSON document are the fields to be indexed, the values + * define the index direction (1 for ascending, -1 for descending). + * + * @return + */ String def(); + /** + * It does not actually make sense to use that attribute as the direction has to be defined in the {@link #def()} + * attribute actually. + * + * @return + */ + @Deprecated IndexDirection direction() default IndexDirection.ASCENDING; boolean unique() default false; @@ -40,8 +55,18 @@ boolean dropDups() default false; + /** + * The name of the index to be created. + * + * @return + */ String name() default ""; + /** + * The collection the index will be created in. Will default to the collection the annotated domain class will be + * stored in. + * + * @return + */ String collection() default ""; - } diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/index/MongoMappingEventPublisher.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/index/MongoMappingEventPublisher.java index bf76048d28..ddd1fcdbc9 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/index/MongoMappingEventPublisher.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/index/MongoMappingEventPublisher.java @@ -1,11 +1,11 @@ /* - * Copyright (c) 2011 by the original author(s). + * Copyright 2011-2012 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, @@ -13,37 +13,51 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package org.springframework.data.mongodb.core.index; +import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationEvent; import org.springframework.context.ApplicationEventPublisher; -import org.springframework.data.mapping.event.MappingContextEvent; +import org.springframework.data.mapping.context.MappingContextEvent; +import org.springframework.data.mongodb.core.MongoTemplate; import org.springframework.data.mongodb.core.mapping.MongoPersistentEntity; import org.springframework.data.mongodb.core.mapping.MongoPersistentProperty; +import org.springframework.data.mongodb.core.mapping.event.AfterLoadEvent; +import org.springframework.data.mongodb.core.mapping.event.AfterSaveEvent; +import org.springframework.util.Assert; /** - * An implementation of ApplicationEventPublisher that will only fire MappingContextEvents for use by the index creator - * when MongoTemplate is used 'stand-alone', that is not declared inside a Spring ApplicationContext. - * - * Declare MongoTemplate inside an ApplicationContext to enable the publishing of all persistence events such as - * {@link AfterLoadEvent}, {@link AfterSaveEvent}, etc. + * An implementation of ApplicationEventPublisher that will only fire {@link MappingContextEvent}s for use by the index + * creator when MongoTemplate is used 'stand-alone', that is not declared inside a Spring {@link ApplicationContext}. + * Declare {@link MongoTemplate} inside an {@link ApplicationContext} to enable the publishing of all persistence events + * such as {@link AfterLoadEvent}, {@link AfterSaveEvent}, etc. * - * @author Jon Brisbin + * @author Jon Brisbin + * @author Oliver Gierke */ public class MongoMappingEventPublisher implements ApplicationEventPublisher { - private MongoPersistentEntityIndexCreator indexCreator; + private final MongoPersistentEntityIndexCreator indexCreator; + /** + * Creates a new {@link MongoMappingEventPublisher} for the given {@link MongoPersistentEntityIndexCreator}. + * + * @param indexCreator must not be {@literal null}. + */ public MongoMappingEventPublisher(MongoPersistentEntityIndexCreator indexCreator) { + + Assert.notNull(indexCreator); this.indexCreator = indexCreator; } + /* + * (non-Javadoc) + * @see org.springframework.context.ApplicationEventPublisher#publishEvent(org.springframework.context.ApplicationEvent) + */ @SuppressWarnings("unchecked") public void publishEvent(ApplicationEvent event) { if (event instanceof MappingContextEvent) { - indexCreator - .onApplicationEvent((MappingContextEvent, MongoPersistentProperty>) event); + indexCreator.onApplicationEvent((MappingContextEvent, MongoPersistentProperty>) event); } } } diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/index/MongoPersistentEntityIndexCreator.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/index/MongoPersistentEntityIndexCreator.java index 547a74dde0..84c342d1f0 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/index/MongoPersistentEntityIndexCreator.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/index/MongoPersistentEntityIndexCreator.java @@ -1,11 +1,11 @@ /* - * Copyright (c) 2011 by the original author(s). + * Copyright 2011-2012 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package org.springframework.data.mongodb.core.index; import java.lang.reflect.Field; @@ -25,9 +24,8 @@ import org.springframework.context.ApplicationListener; import org.springframework.data.mapping.PersistentEntity; import org.springframework.data.mapping.PropertyHandler; -import org.springframework.data.mapping.event.MappingContextEvent; +import org.springframework.data.mapping.context.MappingContextEvent; import org.springframework.data.mongodb.MongoDbFactory; -import org.springframework.data.mongodb.core.mapping.BasicMongoPersistentEntity; import org.springframework.data.mongodb.core.mapping.MongoMappingContext; import org.springframework.data.mongodb.core.mapping.MongoPersistentEntity; import org.springframework.data.mongodb.core.mapping.MongoPersistentProperty; @@ -39,32 +37,35 @@ import com.mongodb.util.JSON; /** - * Component that inspects {@link BasicMongoPersistentEntity} instances contained in the given - * {@link MongoMappingContext} for indexing metadata and ensures the indexes to be available. + * Component that inspects {@link MongoPersistentEntity} instances contained in the given {@link MongoMappingContext} + * for indexing metadata and ensures the indexes to be available. * - * @author Jon Brisbin + * @author Jon Brisbin * @author Oliver Gierke */ public class MongoPersistentEntityIndexCreator implements - ApplicationListener, MongoPersistentProperty>> { + ApplicationListener, MongoPersistentProperty>> { private static final Logger log = LoggerFactory.getLogger(MongoPersistentEntityIndexCreator.class); private final Map, Boolean> classesSeen = new ConcurrentHashMap, Boolean>(); private final MongoDbFactory mongoDbFactory; + private final MongoMappingContext mappingContext; /** * Creats a new {@link MongoPersistentEntityIndexCreator} for the given {@link MongoMappingContext} and * {@link MongoDbFactory}. * - * @param mappingContext must not be {@@iteral null} - * @param mongoDbFactory must not be {@@iteral null} + * @param mappingContext must not be {@literal null} + * @param mongoDbFactory must not be {@literal null} */ public MongoPersistentEntityIndexCreator(MongoMappingContext mappingContext, MongoDbFactory mongoDbFactory) { Assert.notNull(mongoDbFactory); Assert.notNull(mappingContext); + this.mongoDbFactory = mongoDbFactory; + this.mappingContext = mappingContext; for (MongoPersistentEntity entity : mappingContext.getPersistentEntities()) { checkForIndexes(entity); @@ -75,8 +76,11 @@ public MongoPersistentEntityIndexCreator(MongoMappingContext mappingContext, Mon * (non-Javadoc) * @see org.springframework.context.ApplicationListener#onApplicationEvent(org.springframework.context.ApplicationEvent) */ - public void onApplicationEvent( - MappingContextEvent, MongoPersistentProperty> event) { + public void onApplicationEvent(MappingContextEvent, MongoPersistentProperty> event) { + + if (!event.wasEmittedBy(mappingContext)) { + return; + } PersistentEntity entity = event.getPersistentEntity(); @@ -97,12 +101,12 @@ protected void checkForIndexes(final MongoPersistentEntity entity) { if (type.isAnnotationPresent(CompoundIndexes.class)) { CompoundIndexes indexes = type.getAnnotation(CompoundIndexes.class); for (CompoundIndex index : indexes.value()) { - String indexColl = index.collection(); - if ("".equals(indexColl)) { - indexColl = entity.getCollection(); - } - ensureIndex(indexColl, index.name(), index.def(), index.direction(), index.unique(), index.dropDups(), - index.sparse()); + + String indexColl = StringUtils.hasText(index.collection()) ? index.collection() : entity.getCollection(); + DBObject definition = (DBObject) JSON.parse(index.def()); + + ensureIndex(indexColl, index.name(), definition, index.unique(), index.dropDups(), index.sparse()); + if (log.isDebugEnabled()) { log.debug("Created compound index " + index); } @@ -111,10 +115,14 @@ protected void checkForIndexes(final MongoPersistentEntity entity) { entity.doWithProperties(new PropertyHandler() { public void doWithPersistentProperty(MongoPersistentProperty persistentProperty) { + Field field = persistentProperty.getField(); + if (field.isAnnotationPresent(Indexed.class)) { + Indexed index = field.getAnnotation(Indexed.class); String name = index.name(); + if (!StringUtils.hasText(name)) { name = persistentProperty.getFieldName(); } else { @@ -126,11 +134,17 @@ public void doWithPersistentProperty(MongoPersistentProperty persistentProperty) } } } + String collection = StringUtils.hasText(index.collection()) ? index.collection() : entity.getCollection(); - ensureIndex(collection, name, null, index.direction(), index.unique(), index.dropDups(), index.sparse()); + int direction = index.direction() == IndexDirection.ASCENDING ? 1 : -1; + DBObject definition = new BasicDBObject(persistentProperty.getFieldName(), direction); + + ensureIndex(collection, name, definition, index.unique(), index.dropDups(), index.sparse()); + if (log.isDebugEnabled()) { log.debug("Created property index " + index); } + } else if (field.isAnnotationPresent(GeoSpatialIndexed.class)) { GeoSpatialIndexed index = field.getAnnotation(GeoSpatialIndexed.class); @@ -155,21 +169,15 @@ public void doWithPersistentProperty(MongoPersistentProperty persistentProperty) } } - protected void ensureIndex(String collection, final String name, final String def, final IndexDirection direction, - final boolean unique, final boolean dropDups, final boolean sparse) { - DBObject defObj; - if (null != def) { - defObj = (DBObject) JSON.parse(def); - } else { - defObj = new BasicDBObject(); - defObj.put(name, (direction == IndexDirection.ASCENDING ? 1 : -1)); - } + protected void ensureIndex(String collection, String name, DBObject indexDefinition, boolean unique, + boolean dropDups, boolean sparse) { + DBObject opts = new BasicDBObject(); opts.put("name", name); opts.put("dropDups", dropDups); opts.put("sparse", sparse); opts.put("unique", unique); - mongoDbFactory.getDb().getCollection(collection).ensureIndex(defObj, opts); - } + mongoDbFactory.getDb().getCollection(collection).ensureIndex(indexDefinition, opts); + } } diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/mapping/BasicMongoPersistentEntity.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/mapping/BasicMongoPersistentEntity.java index 443babed05..f2db622683 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/mapping/BasicMongoPersistentEntity.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/mapping/BasicMongoPersistentEntity.java @@ -25,6 +25,7 @@ import org.springframework.context.expression.BeanFactoryResolver; import org.springframework.data.mapping.PersistentEntity; import org.springframework.data.mapping.model.BasicPersistentEntity; +import org.springframework.data.mapping.model.MappingException; import org.springframework.data.mongodb.MongoCollectionUtils; import org.springframework.data.util.TypeInformation; import org.springframework.expression.Expression; @@ -46,6 +47,8 @@ public class BasicMongoPersistentEntity extends BasicPersistentEntity typeInformation) { this.collection = fallback; } } + + /* + * (non-Javadoc) + * @see org.springframework.data.mapping.MutablePersistentEntity#addPersistentProperty(P) + */ + @Override + public void addPersistentProperty(MongoPersistentProperty property) { + if (property.isVersion()) { + if (this.versionProperty != null) { + throw new MappingException( + String.format( + "Attempt to add version property %s but already have property %s registered " + + "as version. Check your mapping configuration!", + property.getField(), versionProperty.getField())); + } + this.versionProperty = property; + } + super.addPersistentProperty(property); + } /* * (non-Javadoc) @@ -91,6 +113,14 @@ public String getCollection() { Expression expression = parser.parseExpression(collection, ParserContext.TEMPLATE_EXPRESSION); return expression.getValue(context, String.class); } + + public MongoPersistentProperty getVersionProperty() { + return versionProperty; + } + + public boolean hasVersion() { + return getVersionProperty() != null; + } /** * {@link Comparator} implementation inspecting the {@link MongoPersistentProperty}'s order. @@ -117,5 +147,5 @@ public int compare(MongoPersistentProperty o1, MongoPersistentProperty o2) { return o1.getFieldOrder() - o2.getFieldOrder(); } - } + } } diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/mapping/BasicMongoPersistentProperty.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/mapping/BasicMongoPersistentProperty.java index 5974dee0ce..a2264754aa 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/mapping/BasicMongoPersistentProperty.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/mapping/BasicMongoPersistentProperty.java @@ -16,6 +16,7 @@ package org.springframework.data.mongodb.core.mapping; import java.beans.PropertyDescriptor; +import java.io.ObjectInputStream.GetField; import java.lang.reflect.Field; import java.math.BigInteger; import java.util.HashSet; @@ -142,4 +143,11 @@ public boolean isDbReference() { public DBRef getDBRef() { return getField().getAnnotation(DBRef.class); } + + /* (non-Javadoc) + * @see org.springframework.data.mongodb.core.core.mapping.MongoPersistentProperty#isVersion() + */ + public boolean isVersion() { + return getField().isAnnotationPresent(Version.class); + } } diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/mapping/MongoMappingContext.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/mapping/MongoMappingContext.java index 89210b59ba..6f99ea0912 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/mapping/MongoMappingContext.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/mapping/MongoMappingContext.java @@ -42,6 +42,15 @@ public MongoMappingContext() { setSimpleTypeHolder(MongoSimpleTypes.HOLDER); } + /* + * (non-Javadoc) + * @see org.springframework.data.mapping.context.AbstractMappingContext#shouldCreatePersistentEntityFor(org.springframework.data.util.TypeInformation) + */ + @Override + protected boolean shouldCreatePersistentEntityFor(TypeInformation type) { + return !MongoSimpleTypes.HOLDER.isSimpleType(type.getType()); + } + /* * (non-Javadoc) * @see org.springframework.data.mapping.AbstractMappingContext#createPersistentProperty(java.lang.reflect.Field, java.beans.PropertyDescriptor, org.springframework.data.mapping.MutablePersistentEntity, org.springframework.data.mapping.SimpleTypeHolder) @@ -72,6 +81,7 @@ protected BasicMongoPersistentEntity createPersistentEntity(TypeInformati * (non-Javadoc) * @see org.springframework.context.ApplicationContextAware#setApplicationContext(org.springframework.context.ApplicationContext) */ + @Override public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { this.context = applicationContext; } diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/mapping/MongoPersistentEntity.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/mapping/MongoPersistentEntity.java index 7965ea8520..5a7b16d1b4 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/mapping/MongoPersistentEntity.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/mapping/MongoPersistentEntity.java @@ -9,4 +9,8 @@ public interface MongoPersistentEntity extends PersistentEntity { String getCollection(); + + MongoPersistentProperty getVersionProperty(); + + boolean hasVersion(); } diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/mapping/MongoPersistentProperty.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/mapping/MongoPersistentProperty.java index 0803bffe5d..965f34ae2f 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/mapping/MongoPersistentProperty.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/mapping/MongoPersistentProperty.java @@ -54,6 +54,15 @@ public interface MongoPersistentProperty extends PersistentProperty extends BasicPersistentEntity implements MongoPersistentEntity { @@ -130,5 +138,16 @@ public SimpleMongoPersistentEntity(TypeInformation information) { public String getCollection() { return MongoCollectionUtils.getPreferredCollectionName(getType()); } + + /* (non-Javadoc) + * @see org.springframework.data.mongodb.core.core.mapping.MongoPersistentEntity#getVersionProperty() + */ + public MongoPersistentProperty getVersionProperty() { + return null; + } + + public boolean hasVersion() { + return false; + } } } diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/mapping/Version.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/mapping/Version.java new file mode 100644 index 0000000000..675f4fe7d1 --- /dev/null +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/mapping/Version.java @@ -0,0 +1,18 @@ +package org.springframework.data.mongodb.core.mapping; + +import java.lang.annotation.Documented; +import java.lang.annotation.Retention; +import java.lang.annotation.Target; +import static java.lang.annotation.ElementType.FIELD; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +/** + * Specifies the version field that serves as its optimistic lock value. + * + */ +@Documented +@Target({ FIELD }) +@Retention(RUNTIME) +public @interface Version { + +} diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/query/Criteria.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/query/Criteria.java index 14e0c16554..2874fe2a1c 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/query/Criteria.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/query/Criteria.java @@ -25,7 +25,6 @@ import java.util.regex.Pattern; import org.bson.BSON; -import org.bson.types.BasicBSONList; import org.springframework.data.mongodb.InvalidMongoDbApiUsageException; import org.springframework.data.mongodb.core.geo.Circle; import org.springframework.data.mongodb.core.geo.Point; @@ -33,6 +32,7 @@ import org.springframework.util.Assert; import org.springframework.util.ObjectUtils; +import com.mongodb.BasicDBList; import com.mongodb.BasicDBObject; import com.mongodb.DBObject; @@ -400,7 +400,7 @@ public Criteria elemMatch(Criteria c) { * @param criteria */ public Criteria orOperator(Criteria... criteria) { - BasicBSONList bsonList = createCriteriaList(criteria); + BasicDBList bsonList = createCriteriaList(criteria); criteriaChain.add(new Criteria("$or").is(bsonList)); return this; } @@ -411,7 +411,7 @@ public Criteria orOperator(Criteria... criteria) { * @param criteria */ public Criteria norOperator(Criteria... criteria) { - BasicBSONList bsonList = createCriteriaList(criteria); + BasicDBList bsonList = createCriteriaList(criteria); criteriaChain.add(new Criteria("$nor").is(bsonList)); return this; } @@ -422,7 +422,7 @@ public Criteria norOperator(Criteria... criteria) { * @param criteria */ public Criteria andOperator(Criteria... criteria) { - BasicBSONList bsonList = createCriteriaList(criteria); + BasicDBList bsonList = createCriteriaList(criteria); criteriaChain.add(new Criteria("$and").is(bsonList)); return this; } @@ -478,8 +478,8 @@ protected DBObject getSingleCriteriaObject() { return queryCriteria; } - private BasicBSONList createCriteriaList(Criteria[] criteria) { - BasicBSONList bsonList = new BasicBSONList(); + private BasicDBList createCriteriaList(Criteria[] criteria) { + BasicDBList bsonList = new BasicDBList(); for (Criteria c : criteria) { bsonList.add(c.getCriteriaObject()); } @@ -514,9 +514,28 @@ public boolean equals(Object obj) { Criteria that = (Criteria) obj; - boolean keyEqual = this.key == null ? that.key == null : this.key.equals(that.key); - boolean criteriaEqual = this.criteria.equals(that.criteria); - boolean valueEqual = isEqual(this.isValue, that.isValue); + if (this.criteriaChain.size() != that.criteriaChain.size()) { + return false; + } + + for (int i = 0; i < this.criteriaChain.size(); i++) { + + Criteria left = this.criteriaChain.get(i); + Criteria right = that.criteriaChain.get(i); + + if (!simpleCriteriaEquals(left, right)) { + return false; + } + } + + return true; + } + + private boolean simpleCriteriaEquals(Criteria left, Criteria right) { + + boolean keyEqual = left.key == null ? right.key == null : left.key.equals(right.key); + boolean criteriaEqual = left.criteria.equals(right.criteria); + boolean valueEqual = isEqual(left.isValue, right.isValue); return keyEqual && criteriaEqual && valueEqual; } diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/query/Update.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/query/Update.java index dd7c7fcc3d..c90c7bc664 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/query/Update.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/query/Update.java @@ -15,8 +15,10 @@ */ package org.springframework.data.mongodb.core.query; +import java.util.Arrays; import java.util.HashMap; import java.util.LinkedHashMap; +import java.util.List; import com.mongodb.BasicDBObject; import com.mongodb.DBObject; @@ -39,6 +41,18 @@ public enum Position { public static Update update(String key, Object value) { return new Update().set(key, value); } + + public static Update fromDBObject(DBObject object, String... exclude) { + Update update = new Update(); + List excludeList = Arrays.asList(exclude); + for (String key : object.keySet()) { + if(excludeList.contains(key)) { + continue; + } + update.set(key, object.get(key)); + } + return update; + } /** * Update using the $set update modifier diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/config/EnableMongoRepositories.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/config/EnableMongoRepositories.java new file mode 100644 index 0000000000..dc0fbb91c0 --- /dev/null +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/config/EnableMongoRepositories.java @@ -0,0 +1,122 @@ +/* + * Copyright 2012 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.data.mongodb.repository.config; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Inherited; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +import org.springframework.beans.factory.FactoryBean; +import org.springframework.context.annotation.ComponentScan.Filter; +import org.springframework.context.annotation.Import; +import org.springframework.data.mongodb.core.MongoTemplate; +import org.springframework.data.mongodb.repository.support.MongoRepositoryFactoryBean; +import org.springframework.data.repository.query.QueryLookupStrategy; +import org.springframework.data.repository.query.QueryLookupStrategy.Key; + +/** + * Annotation to activate MongoDB repositories. If no base package is configured through either {@link #value()}, + * {@link #basePackages()} or {@link #basePackageClasses()} it will trigger scanning of the package of annotated class. + * + * @author Oliver Gierke + */ +@Target(ElementType.TYPE) +@Retention(RetentionPolicy.RUNTIME) +@Documented +@Inherited +@Import(MongoRepositoriesRegistrar.class) +public @interface EnableMongoRepositories { + + /** + * Alias for the {@link #basePackages()} attribute. Allows for more concise annotation declarations e.g.: + * {@code @EnableJpaRepositories("org.my.pkg")} instead of {@code @EnableJpaRepositories(basePackages="org.my.pkg")}. + */ + String[] value() default {}; + + /** + * Base packages to scan for annotated components. {@link #value()} is an alias for (and mutually exclusive with) this + * attribute. Use {@link #basePackageClasses()} for a type-safe alternative to String-based package names. + */ + String[] basePackages() default {}; + + /** + * Type-safe alternative to {@link #basePackages()} for specifying the packages to scan for annotated components. The + * package of each class specified will be scanned. Consider creating a special no-op marker class or interface in + * each package that serves no purpose other than being referenced by this attribute. + */ + Class[] basePackageClasses() default {}; + + /** + * Specifies which types are eligible for component scanning. Further narrows the set of candidate components from + * everything in {@link #basePackages()} to everything in the base packages that matches the given filter or filters. + */ + Filter[] includeFilters() default {}; + + /** + * Specifies which types are not eligible for component scanning. + */ + Filter[] excludeFilters() default {}; + + /** + * Returns the postfix to be used when looking up custom repository implementations. Defaults to {@literal Impl}. So + * for a repository named {@code PersonRepository} the corresponding implementation class will be looked up scanning + * for {@code PersonRepositoryImpl}. + * + * @return + */ + String repositoryImplementationPostfix() default ""; + + /** + * Configures the location of where to find the Spring Data named queries properties file. Will default to + * {@code META-INFO/mongo-named-queries.properties}. + * + * @return + */ + String namedQueriesLocation() default ""; + + /** + * Returns the key of the {@link QueryLookupStrategy} to be used for lookup queries for query methods. Defaults to + * {@link Key#CREATE_IF_NOT_FOUND}. + * + * @return + */ + Key queryLookupStrategy() default Key.CREATE_IF_NOT_FOUND; + + /** + * Returns the {@link FactoryBean} class to be used for each repository instance. Defaults to + * {@link MongoRepositoryFactoryBean}. + * + * @return + */ + Class repositoryFactoryBeanClass() default MongoRepositoryFactoryBean.class; + + /** + * Configures the name of the {@link MongoTemplate} bean to be used with the repositories detected. + * + * @return + */ + String mongoTemplateRef() default "mongoTemplate"; + + /** + * Whether to automatically create indexes for query methods defined in the repository interface. + * + * @return + */ + boolean createIndexesForQueryMethods() default false; +} diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/config/MongoRepositoriesRegistrar.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/config/MongoRepositoriesRegistrar.java new file mode 100644 index 0000000000..96b8c317ab --- /dev/null +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/config/MongoRepositoriesRegistrar.java @@ -0,0 +1,48 @@ +/* + * Copyright 2012 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.data.mongodb.repository.config; + +import java.lang.annotation.Annotation; + +import org.springframework.context.annotation.ImportBeanDefinitionRegistrar; +import org.springframework.data.repository.config.RepositoryBeanDefinitionRegistrarSupport; +import org.springframework.data.repository.config.RepositoryConfigurationExtension; + +/** + * Mongo-specific {@link ImportBeanDefinitionRegistrar}. + * + * @author Oliver Gierke + */ +class MongoRepositoriesRegistrar extends RepositoryBeanDefinitionRegistrarSupport { + + /* + * (non-Javadoc) + * @see org.springframework.data.repository.config.RepositoryBeanDefinitionRegistrarSupport#getAnnotation() + */ + @Override + protected Class getAnnotation() { + return EnableMongoRepositories.class; + } + + /* + * (non-Javadoc) + * @see org.springframework.data.repository.config.RepositoryBeanDefinitionRegistrarSupport#getExtension() + */ + @Override + protected RepositoryConfigurationExtension getExtension() { + return new MongoRepositoryConfigurationExtension(); + } +} diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/config/MongoRepositoryConfigParser.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/config/MongoRepositoryConfigParser.java deleted file mode 100644 index 5c4841f03a..0000000000 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/config/MongoRepositoryConfigParser.java +++ /dev/null @@ -1,54 +0,0 @@ -/* - * Copyright 2011 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.springframework.data.mongodb.repository.config; - -import org.springframework.beans.factory.support.BeanDefinitionBuilder; -import org.springframework.beans.factory.support.BeanDefinitionRegistry; -import org.springframework.data.mongodb.repository.config.SimpleMongoRepositoryConfiguration.MongoRepositoryConfiguration; -import org.springframework.data.repository.config.AbstractRepositoryConfigDefinitionParser; -import org.w3c.dom.Element; - -/** - * {@link org.springframework.beans.factory.xml.BeanDefinitionParser} to create Mongo DB repositories from classpath - * scanning or manual definition. - * - * @author Oliver Gierke - */ -public class MongoRepositoryConfigParser extends - AbstractRepositoryConfigDefinitionParser { - - /* - * (non-Javadoc) - * @see org.springframework.data.repository.config.AbstractRepositoryConfigDefinitionParser#getGlobalRepositoryConfigInformation(org.w3c.dom.Element) - */ - @Override - protected SimpleMongoRepositoryConfiguration getGlobalRepositoryConfigInformation(Element element) { - - return new SimpleMongoRepositoryConfiguration(element); - } - - /* - * (non-Javadoc) - * @see org.springframework.data.repository.config.AbstractRepositoryConfigDefinitionParser#postProcessBeanDefinition(org.springframework.data.repository.config.SingleRepositoryConfigInformation, org.springframework.beans.factory.support.BeanDefinitionBuilder, org.springframework.beans.factory.support.BeanDefinitionRegistry, java.lang.Object) - */ - @Override - protected void postProcessBeanDefinition(MongoRepositoryConfiguration context, BeanDefinitionBuilder builder, - BeanDefinitionRegistry registry, Object beanSource) { - - builder.addPropertyReference("mongoOperations", context.getMongoTemplateRef()); - builder.addPropertyValue("createIndexesForQueryMethods", context.getCreateQueryIndexes()); - } -} diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/config/MongoRepositoryConfigurationExtension.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/config/MongoRepositoryConfigurationExtension.java new file mode 100644 index 0000000000..6963a9fa1d --- /dev/null +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/config/MongoRepositoryConfigurationExtension.java @@ -0,0 +1,80 @@ +/* + * Copyright 2012 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.data.mongodb.repository.config; + +import org.springframework.beans.factory.support.BeanDefinitionBuilder; +import org.springframework.core.annotation.AnnotationAttributes; +import org.springframework.data.config.ParsingUtils; +import org.springframework.data.mongodb.repository.support.MongoRepositoryFactoryBean; +import org.springframework.data.repository.config.AnnotationRepositoryConfigurationSource; +import org.springframework.data.repository.config.RepositoryConfigurationExtension; +import org.springframework.data.repository.config.RepositoryConfigurationExtensionSupport; +import org.springframework.data.repository.config.XmlRepositoryConfigurationSource; +import org.w3c.dom.Element; + +/** + * {@link RepositoryConfigurationExtension} for MongoDB. + * + * @author Oliver Gierke + */ +public class MongoRepositoryConfigurationExtension extends RepositoryConfigurationExtensionSupport { + + private static final String MONGO_TEMPLATE_REF = "mongo-template-ref"; + private static final String CREATE_QUERY_INDEXES = "create-query-indexes"; + + /* + * (non-Javadoc) + * @see org.springframework.data.repository.config.RepositoryConfigurationExtensionSupport#getModulePrefix() + */ + @Override + protected String getModulePrefix() { + return "mongo"; + } + + /* + * (non-Javadoc) + * @see org.springframework.data.repository.config.RepositoryConfigurationExtension#getRepositoryFactoryClassName() + */ + public String getRepositoryFactoryClassName() { + return MongoRepositoryFactoryBean.class.getName(); + } + + /* + * (non-Javadoc) + * @see org.springframework.data.repository.config.RepositoryConfigurationExtensionSupport#postProcess(org.springframework.beans.factory.support.BeanDefinitionBuilder, org.springframework.data.repository.config.XmlRepositoryConfigurationSource) + */ + @Override + public void postProcess(BeanDefinitionBuilder builder, XmlRepositoryConfigurationSource config) { + + Element element = config.getElement(); + + ParsingUtils.setPropertyReference(builder, element, MONGO_TEMPLATE_REF, "mongoOperations"); + ParsingUtils.setPropertyValue(builder, element, CREATE_QUERY_INDEXES, "createIndexesForQueryMethods"); + } + + /* + * (non-Javadoc) + * @see org.springframework.data.repository.config.RepositoryConfigurationExtensionSupport#postProcess(org.springframework.beans.factory.support.BeanDefinitionBuilder, org.springframework.data.repository.config.AnnotationRepositoryConfigurationSource) + */ + @Override + public void postProcess(BeanDefinitionBuilder builder, AnnotationRepositoryConfigurationSource config) { + + AnnotationAttributes attributes = config.getAttributes(); + + builder.addPropertyReference("mongoOperations", attributes.getString("mongoTemplateRef")); + builder.addPropertyValue("createIndexesForQueryMethods", attributes.getBoolean("createIndexesForQueryMethods")); + } +} diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/config/SimpleMongoRepositoryConfiguration.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/config/SimpleMongoRepositoryConfiguration.java deleted file mode 100644 index 7fe5af2cea..0000000000 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/config/SimpleMongoRepositoryConfiguration.java +++ /dev/null @@ -1,196 +0,0 @@ -/* - * Copyright 2011 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.springframework.data.mongodb.repository.config; - -import org.springframework.data.mongodb.repository.support.MongoRepositoryFactoryBean; -import org.springframework.data.repository.config.AutomaticRepositoryConfigInformation; -import org.springframework.data.repository.config.ManualRepositoryConfigInformation; -import org.springframework.data.repository.config.RepositoryConfig; -import org.springframework.data.repository.config.SingleRepositoryConfigInformation; -import org.springframework.util.StringUtils; -import org.w3c.dom.Element; - -/** - * {@link RepositoryConfig} implementation to create {@link MongoRepositoryConfiguration} instances for both automatic - * and manual configuration. - * - * @author Oliver Gierke - */ -public class SimpleMongoRepositoryConfiguration - extends - RepositoryConfig { - - private static final String MONGO_TEMPLATE_REF = "mongo-template-ref"; - private static final String CREATE_QUERY_INDEXES = "create-query-indexes"; - private static final String DEFAULT_MONGO_TEMPLATE_REF = "mongoTemplate"; - - /** - * Creates a new {@link SimpleMongoRepositoryConfiguration} for the given {@link Element}. - * - * @param repositoriesElement - */ - protected SimpleMongoRepositoryConfiguration(Element repositoriesElement) { - - super(repositoriesElement, MongoRepositoryFactoryBean.class.getName()); - } - - /** - * Returns the bean name of the {@link org.springframework.data.mongodb.core.core.MongoTemplate} to be referenced. - * - * @return - */ - public String getMongoTemplateRef() { - - String templateRef = getSource().getAttribute(MONGO_TEMPLATE_REF); - return StringUtils.hasText(templateRef) ? templateRef : DEFAULT_MONGO_TEMPLATE_REF; - } - - /** - * Returns whether to create indexes for query methods. - * - * @return - */ - public boolean getCreateQueryIndexes() { - - String createQueryIndexes = getSource().getAttribute(CREATE_QUERY_INDEXES); - return StringUtils.hasText(createQueryIndexes) ? Boolean.parseBoolean(createQueryIndexes) : false; - } - - /* - * (non-Javadoc) - * - * @see - * org.springframework.data.repository.config.GlobalRepositoryConfigInformation - * #getAutoconfigRepositoryInformation(java.lang.String) - */ - public MongoRepositoryConfiguration getAutoconfigRepositoryInformation(String interfaceName) { - - return new AutomaticMongoRepositoryConfiguration(interfaceName, this); - } - - /* - * (non-Javadoc) - * @see org.springframework.data.repository.config.RepositoryConfig#getNamedQueriesLocation() - */ - public String getNamedQueriesLocation() { - return "classpath*:META-INF/mongo-named-queries.properties"; - } - - /* - * (non-Javadoc) - * - * @see org.springframework.data.repository.config.RepositoryConfig# - * createSingleRepositoryConfigInformationFor(org.w3c.dom.Element) - */ - @Override - protected MongoRepositoryConfiguration createSingleRepositoryConfigInformationFor(Element element) { - - return new ManualMongoRepositoryConfiguration(element, this); - } - - /** - * Simple interface for configuration values specific to Mongo repositories. - * - * @author Oliver Gierke - */ - public interface MongoRepositoryConfiguration extends - SingleRepositoryConfigInformation { - - String getMongoTemplateRef(); - - boolean getCreateQueryIndexes(); - } - - /** - * Implements manual lookup of the additional attributes. - * - * @author Oliver Gierke - */ - private static class ManualMongoRepositoryConfiguration extends - ManualRepositoryConfigInformation implements MongoRepositoryConfiguration { - - /** - * Creates a new {@link ManualMongoRepositoryConfiguration} for the given {@link Element} and parent. - * - * @param element - * @param parent - */ - public ManualMongoRepositoryConfiguration(Element element, SimpleMongoRepositoryConfiguration parent) { - - super(element, parent); - } - - /* - * (non-Javadoc) - * - * @see org.springframework.data.mongodb.repository.config. - * SimpleMongoRepositoryConfiguration - * .MongoRepositoryConfiguration#getMongoTemplateRef() - */ - public String getMongoTemplateRef() { - - return getAttribute(MONGO_TEMPLATE_REF); - } - - /* (non-Javadoc) - * @see org.springframework.data.mongodb.config.SimpleMongoRepositoryConfiguration.MongoRepositoryConfiguration#getCreateQueryIndexes() - */ - public boolean getCreateQueryIndexes() { - - String attribute = getAttribute(CREATE_QUERY_INDEXES); - return attribute == null ? false : Boolean.parseBoolean(attribute); - } - } - - /** - * Implements the lookup of the additional attributes during automatic configuration. - * - * @author Oliver Gierke - */ - private static class AutomaticMongoRepositoryConfiguration extends - AutomaticRepositoryConfigInformation implements MongoRepositoryConfiguration { - - /** - * Creates a new {@link AutomaticMongoRepositoryConfiguration} for the given interface and parent. - * - * @param interfaceName - * @param parent - */ - public AutomaticMongoRepositoryConfiguration(String interfaceName, SimpleMongoRepositoryConfiguration parent) { - - super(interfaceName, parent); - } - - /* - * (non-Javadoc) - * - * @see org.springframework.data.mongodb.repository.config. - * SimpleMongoRepositoryConfiguration - * .MongoRepositoryConfiguration#getMongoTemplateRef() - */ - public String getMongoTemplateRef() { - - return getParent().getMongoTemplateRef(); - } - - /* (non-Javadoc) - * @see org.springframework.data.mongodb.config.SimpleMongoRepositoryConfiguration.MongoRepositoryConfiguration#getCreateQueryIndexes() - */ - public boolean getCreateQueryIndexes() { - return getParent().getCreateQueryIndexes(); - } - } -} diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/ConvertingParameterAccessor.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/ConvertingParameterAccessor.java index 8d9c6b1b07..98a06f3294 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/ConvertingParameterAccessor.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/ConvertingParameterAccessor.java @@ -15,7 +15,11 @@ */ package org.springframework.data.mongodb.repository.query; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; import java.util.Iterator; +import java.util.List; import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Sort; @@ -25,6 +29,9 @@ import org.springframework.data.mongodb.core.mapping.MongoPersistentProperty; import org.springframework.data.repository.query.ParameterAccessor; import org.springframework.util.Assert; +import org.springframework.util.CollectionUtils; + +import com.mongodb.DBRef; /** * Custom {@link ParameterAccessor} that uses a {@link MongoWriter} to serialize parameters into Mongo format. @@ -158,7 +165,28 @@ public Object next() { * @see org.springframework.data.mongodb.repository.ConvertingParameterAccessor.PotentiallConvertingIterator#nextConverted() */ public Object nextConverted(MongoPersistentProperty property) { - return property.isAssociation() ? writer.toDBRef(next(), property) : getConvertedValue(next()); + + Object next = next(); + + if (next == null) { + return null; + } + + if (property.isAssociation()) { + if (next.getClass().isArray() || next instanceof Iterable) { + + List dbRefs = new ArrayList(); + for (Object element : asCollection(next)) { + dbRefs.add(writer.toDBRef(element, property)); + } + + return dbRefs; + } else { + return writer.toDBRef(next, property); + } + } + + return getConvertedValue(next); } /* @@ -170,6 +198,33 @@ public void remove() { } } + /** + * Returns the given object as {@link Collection}. Will do a copy of it if it implements {@link Iterable} or is an + * array. Will return an empty {@link Collection} in case {@literal null} is given. Will wrap all other types into a + * single-element collction + * + * @param source + * @return + */ + private static Collection asCollection(Object source) { + + if (source instanceof Iterable) { + + List result = new ArrayList(); + for (Object element : (Iterable) source) { + result.add(element); + } + + return result; + } + + if (source == null) { + return Collections.emptySet(); + } + + return source.getClass().isArray() ? CollectionUtils.arrayToList(source) : Collections.singleton(source); + } + /** * Custom {@link Iterator} that adds a method to access elements in a converted manner. * diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/MongoEntityInformation.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/MongoEntityInformation.java index a68896c03d..3b214fbc3b 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/MongoEntityInformation.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/MongoEntityInformation.java @@ -39,4 +39,30 @@ public interface MongoEntityInformation extends Enti * @return */ String getIdAttribute(); + + + /** + * Returns the attribute that the version will be persisted to. + * + * @return + */ + String getVersionAttribute(); + + + /** + * Returns the entity version. + * + * @return + */ + Object getVersion(T entity); + + + + /** + * Returns whether entity has version. + * + * @param entity + * @return + */ + boolean hasVersion(T entity); } \ No newline at end of file diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/MongoQueryCreator.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/MongoQueryCreator.java index 6bdb4ced40..00c5c91282 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/MongoQueryCreator.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/MongoQueryCreator.java @@ -228,7 +228,7 @@ private Criteria from(Type type, MongoPersistentProperty property, Criteria crit case SIMPLE_PROPERTY: return criteria.is(parameters.nextConverted(property)); case NEGATING_SIMPLE_PROPERTY: - return criteria.not().is(parameters.nextConverted(property)); + return criteria.ne(parameters.nextConverted(property)); } throw new IllegalArgumentException("Unsupported keyword!"); diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/support/MappingMongoEntityInformation.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/support/MappingMongoEntityInformation.java index 8795794641..38a1f08688 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/support/MappingMongoEntityInformation.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/support/MappingMongoEntityInformation.java @@ -71,6 +71,30 @@ public ID getId(T entity) { throw new RuntimeException(e); } } + + /* (non-Javadoc) + * @see org.springframework.data.mongodb.repository.query.MongoEntityInformation#getVersion(T) + */ + public Object getVersion(T entity) { + MongoPersistentProperty versionProperty = entityMetadata.getVersionProperty(); + try { + return BeanWrapper.create(entity, null).getProperty(versionProperty); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + /* (non-Javadoc) + * @see org.springframework.data.mongodb.repository.query.MongoEntityInformation#hasVersion(T) + */ + public boolean hasVersion(T entity) { + try { + getVersion(entity); + return true; + } catch (Exception e) { + return false; + } + } /* (non-Javadoc) * @see org.springframework.data.repository.support.EntityInformation#getIdType() @@ -93,4 +117,11 @@ public String getCollectionName() { public String getIdAttribute() { return entityMetadata.getIdProperty().getName(); } + + /* (non-Javadoc) + * @see org.springframework.data.mongodb.repository.MongoEntityInformation#getVersionAttribute() + */ + public String getVersionAttribute() { + return entityMetadata.getVersionProperty().getName(); + } } diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/support/SimpleMongoRepository.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/support/SimpleMongoRepository.java index 757082bd1d..b171e6b661 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/support/SimpleMongoRepository.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/support/SimpleMongoRepository.java @@ -115,8 +115,11 @@ private Criteria getIdCriteria(Object id) { public boolean exists(ID id) { Assert.notNull(id, "The given id must not be null!"); - return mongoOperations.findOne(new Query(Criteria.where("_id").is(id)), Object.class, - entityInformation.getCollectionName()) != null; + + final Query idQuery = getIdQuery(id); + idQuery.fields(); + + return mongoOperations.findOne(idQuery, entityInformation.getJavaType(), entityInformation.getCollectionName()) != null; } /* diff --git a/spring-data-mongodb/src/main/resources/org/springframework/data/mongodb/config/spring-mongo-1.0.xsd b/spring-data-mongodb/src/main/resources/org/springframework/data/mongodb/config/spring-mongo-1.0.xsd index 702c855b03..a027962ae9 100644 --- a/spring-data-mongodb/src/main/resources/org/springframework/data/mongodb/config/spring-mongo-1.0.xsd +++ b/spring-data-mongodb/src/main/resources/org/springframework/data/mongodb/config/spring-mongo-1.0.xsd @@ -14,7 +14,7 @@ + schemaLocation="http://www.springframework.org/schema/data/repository/spring-repository-1.0.xsd" /> diff --git a/spring-data-mongodb/src/main/resources/org/springframework/data/mongodb/config/spring-mongo-1.1.xsd b/spring-data-mongodb/src/main/resources/org/springframework/data/mongodb/config/spring-mongo-1.1.xsd index feae5468bc..9bfbb8799f 100644 --- a/spring-data-mongodb/src/main/resources/org/springframework/data/mongodb/config/spring-mongo-1.1.xsd +++ b/spring-data-mongodb/src/main/resources/org/springframework/data/mongodb/config/spring-mongo-1.1.xsd @@ -103,15 +103,6 @@ The Mongo URI string.]]> - - - - - - - - - @@ -134,9 +125,6 @@ The Mongo URI string.]]> - - - diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/config/AbstractMongoConfigurationUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/config/AbstractMongoConfigurationUnitTests.java new file mode 100644 index 0000000000..9423978b2a --- /dev/null +++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/config/AbstractMongoConfigurationUnitTests.java @@ -0,0 +1,97 @@ +/* + * Copyright 2012 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.data.mongodb.config; + +import static org.hamcrest.Matchers.*; +import static org.junit.Assert.*; + +import org.junit.Test; +import org.springframework.context.annotation.Bean; +import org.springframework.data.mongodb.core.mapping.Document; + +import com.mongodb.Mongo; + +/** + * Unit tests for {@link AbstractMongoConfiguration}. + * + * @author Oliver Gierke + */ +public class AbstractMongoConfigurationUnitTests { + + /** + * @see DATAMONGO-496 + */ + @Test + public void usesConfigClassPackageAsBaseMappingPackage() throws ClassNotFoundException { + + AbstractMongoConfiguration configuration = new SampleMongoConfiguration(); + assertThat(configuration.getMappingBasePackage(), is(SampleMongoConfiguration.class.getPackage().getName())); + assertThat(configuration.getInitialEntitySet(), hasSize(1)); + assertThat(configuration.getInitialEntitySet(), hasItem(Entity.class)); + } + + /** + * @see DATAMONGO-496 + */ + @Test + public void doesNotScanPackageIfMappingPackageIsNull() throws ClassNotFoundException { + + assertScanningDisabled(null); + + } + + /** + * @see DATAMONGO-496 + */ + @Test + public void doesNotScanPackageIfMappingPackageIsEmpty() throws ClassNotFoundException { + + assertScanningDisabled(""); + assertScanningDisabled(" "); + } + + private static void assertScanningDisabled(final String value) throws ClassNotFoundException { + + AbstractMongoConfiguration configuration = new SampleMongoConfiguration() { + @Override + protected String getMappingBasePackage() { + return value; + } + }; + + assertThat(configuration.getMappingBasePackage(), is(value)); + assertThat(configuration.getInitialEntitySet(), hasSize(0)); + } + + static class SampleMongoConfiguration extends AbstractMongoConfiguration { + + @Override + protected String getDatabaseName() { + return "database"; + } + + @Bean + @Override + public Mongo mongo() throws Exception { + return new Mongo(); + } + } + + @Document + static class Entity { + + } +} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/DBObjectUtils.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/DBObjectUtils.java new file mode 100644 index 0000000000..a9be3ba3af --- /dev/null +++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/DBObjectUtils.java @@ -0,0 +1,81 @@ +/* + * Copyright 2012 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.data.mongodb.core; + +import static org.hamcrest.Matchers.*; +import static org.junit.Assert.*; + +import com.mongodb.BasicDBList; +import com.mongodb.DBObject; + +/** + * Helper classes to ease assertions on {@link DBObject}s. + * + * @author Oliver Gierke + */ +public abstract class DBObjectUtils { + + private DBObjectUtils() { + + } + + /** + * Expects the field with the given key to be not {@literal null} and a {@link DBObject} in turn and returns it. + * + * @param source the {@link DBObject} to lookup the nested one + * @param key the key of the field to lokup the nested {@link DBObject} + * @return + */ + public static DBObject getAsDBObject(DBObject source, String key) { + return getTypedValue(source, key, DBObject.class); + } + + /** + * Expects the field with the given key to be not {@literal null} and a {@link BasicDBList}. + * + * @param source the {@link DBObject} to lookup the {@link BasicDBList} in + * @param key the key of the field to find the {@link BasicDBList} in + * @return + */ + public static BasicDBList getAsDBList(DBObject source, String key) { + return getTypedValue(source, key, BasicDBList.class); + } + + /** + * Expects the list element with the given index to be a non-{@literal null} {@link DBObject} and returns it. + * + * @param source the {@link BasicDBList} to look up the {@link DBObject} element in + * @param index the index of the element expected to contain a {@link DBObject} + * @return + */ + public static DBObject getAsDBObject(BasicDBList source, int index) { + + assertThat(source.size(), greaterThanOrEqualTo(index + 1)); + Object value = source.get(index); + assertThat(value, is(instanceOf(DBObject.class))); + return (DBObject) value; + } + + @SuppressWarnings("unchecked") + private static T getTypedValue(DBObject source, String key, Class type) { + + Object value = source.get(key); + assertThat(value, is(notNullValue())); + assertThat(value, is(instanceOf(type))); + + return (T) value; + } +} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/MongoTemplateTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/MongoTemplateTests.java index 92fd4847a8..b2e5ee5798 100644 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/MongoTemplateTests.java +++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/MongoTemplateTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2011 the original author or authors. + * Copyright 2011-2012 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -37,9 +37,10 @@ import org.junit.rules.ExpectedException; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.context.ApplicationContext; import org.springframework.core.convert.converter.Converter; import org.springframework.dao.DataAccessException; +import org.springframework.dao.DataIntegrityViolationException; +import org.springframework.dao.OptimisticLockingFailureException; import org.springframework.data.annotation.Id; import org.springframework.data.annotation.PersistenceConstructor; import org.springframework.data.mongodb.InvalidMongoDbApiUsageException; @@ -51,6 +52,7 @@ import org.springframework.data.mongodb.core.index.IndexField; import org.springframework.data.mongodb.core.index.IndexInfo; import org.springframework.data.mongodb.core.mapping.MongoMappingContext; +import org.springframework.data.mongodb.core.query.BasicQuery; import org.springframework.data.mongodb.core.query.Criteria; import org.springframework.data.mongodb.core.query.Order; import org.springframework.data.mongodb.core.query.Query; @@ -73,6 +75,7 @@ * * @author Oliver Gierke * @author Thomas Risberg + * @author Amol Nayak */ @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration("classpath:infrastructure.xml") @@ -122,18 +125,20 @@ public void cleanUp() { } protected void cleanDb() { - template.dropCollection(template.getCollectionName(Person.class)); - template.dropCollection(template.getCollectionName(PersonWithAList.class)); - template.dropCollection(template.getCollectionName(PersonWith_idPropertyOfTypeObjectId.class)); - template.dropCollection(template.getCollectionName(PersonWith_idPropertyOfTypeString.class)); - template.dropCollection(template.getCollectionName(PersonWithIdPropertyOfTypeObjectId.class)); - template.dropCollection(template.getCollectionName(PersonWithIdPropertyOfTypeString.class)); - template.dropCollection(template.getCollectionName(PersonWithIdPropertyOfTypeInteger.class)); - template.dropCollection(template.getCollectionName(PersonWithIdPropertyOfPrimitiveInt.class)); - template.dropCollection(template.getCollectionName(PersonWithIdPropertyOfTypeLong.class)); - template.dropCollection(template.getCollectionName(PersonWithIdPropertyOfPrimitiveLong.class)); - template.dropCollection(template.getCollectionName(TestClass.class)); + template.dropCollection(Person.class); + template.dropCollection(PersonWithAList.class); + template.dropCollection(PersonWith_idPropertyOfTypeObjectId.class); + template.dropCollection(PersonWith_idPropertyOfTypeString.class); + template.dropCollection(PersonWithIdPropertyOfTypeObjectId.class); + template.dropCollection(PersonWithIdPropertyOfTypeString.class); + template.dropCollection(PersonWithIdPropertyOfTypeInteger.class); + template.dropCollection(PersonWithIdPropertyOfPrimitiveInt.class); + template.dropCollection(PersonWithIdPropertyOfTypeLong.class); + template.dropCollection(PersonWithIdPropertyOfPrimitiveLong.class); + template.dropCollection(PersonWithVersionPropertyOfTypeInteger.class); + template.dropCollection(TestClass.class); template.dropCollection(Sample.class); + template.dropCollection(MyPerson.class); } @Test @@ -163,6 +168,112 @@ public void bogusUpdateDoesNotTriggerException() throws Exception { mongoTemplate.updateFirst(q, u, Person.class); } + /** + * @see DATAMONGO-480 + */ + @Test + public void throwsExceptionForDuplicateIds() { + + MongoTemplate template = new MongoTemplate(factory); + template.setWriteResultChecking(WriteResultChecking.EXCEPTION); + + Person person = new Person(new ObjectId(), "Amol"); + person.setAge(28); + + template.insert(person); + + try { + template.insert(person); + fail("Expected DataIntegrityViolationException!"); + } catch (DataIntegrityViolationException e) { + assertThat(e.getMessage(), containsString("E11000 duplicate key error index: database.person.$_id_ dup key:")); + } + } + + /** + * @see DATAMONGO-480 + */ + @Test + public void throwsExceptionForUpdateWithInvalidPushOperator() { + + MongoTemplate template = new MongoTemplate(factory); + template.setWriteResultChecking(WriteResultChecking.EXCEPTION); + + ObjectId id = new ObjectId(); + Person person = new Person(id, "Amol"); + person.setAge(28); + + template.insert(person); + + try { + + Query query = new Query(Criteria.where("firstName").is("Amol")); + Update upd = new Update().push("age", 29); + template.updateFirst(query, upd, Person.class); + fail("Expected DataIntegrityViolationException!"); + + } catch (DataIntegrityViolationException e) { + + assertThat(e.getMessage(), + is("Execution of update with '{ \"$push\" : { \"age\" : 29}}'' using '{ \"firstName\" : \"Amol\"}' " + + "query failed: Cannot apply $push/$pushAll modifier to non-array")); + } + } + + /** + * @see DATAMONGO-480 + */ + @Test + public void throwsExceptionForIndexViolationIfConfigured() { + + MongoTemplate template = new MongoTemplate(factory); + template.setWriteResultChecking(WriteResultChecking.EXCEPTION); + template.indexOps(Person.class).ensureIndex(new Index().on("firstName", Order.DESCENDING).unique()); + + Person person = new Person(new ObjectId(), "Amol"); + person.setAge(28); + + template.save(person); + + person = new Person(new ObjectId(), "Amol"); + person.setAge(28); + + try { + template.save(person); + fail("Expected DataIntegrityViolationException!"); + } catch (DataIntegrityViolationException e) { + assertThat(e.getMessage(), + containsString("E11000 duplicate key error index: database.person.$firstName_-1 dup key:")); + } + } + + /** + * @see DATAMONGO-480 + */ + @Test + public void rejectsDuplicateIdInInsertAll() { + + MongoTemplate template = new MongoTemplate(factory); + template.setWriteResultChecking(WriteResultChecking.EXCEPTION); + + ObjectId id = new ObjectId(); + Person person = new Person(id, "Amol"); + person.setAge(28); + + List records = new ArrayList(); + records.add(person); + records.add(person); + + try { + template.insertAll(records); + fail("Expected DataIntegrityViolationException!"); + } catch (DataIntegrityViolationException e) { + assertThat( + e.getMessage(), + startsWith("Insert list failed: E11000 duplicate key error index: database.person.$_id_ dup key: { : ObjectId")); + } + } + @Test public void testEnsureIndex() throws Exception { @@ -193,7 +304,7 @@ public void testEnsureIndex() throws Exception { assertThat(dropDupes, is(true)); List indexInfoList = template.indexOps(Person.class).getIndexInfo(); - System.out.println(indexInfoList); + assertThat(indexInfoList.size(), is(2)); IndexInfo ii = indexInfoList.get(1); assertThat(ii.isUnique(), is(true)); @@ -939,7 +1050,7 @@ public void updatesDBRefsCorrectly() { DBRef first = new DBRef(factory.getDb(), "foo", new ObjectId()); DBRef second = new DBRef(factory.getDb(), "bar", new ObjectId()); - template.updateFirst(null, Update.update("dbRefs", Arrays.asList(first, second)), ClassWithDBRefs.class); + template.updateFirst(null, update("dbRefs", Arrays.asList(first, second)), ClassWithDBRefs.class); } class ClassWithDBRefs { @@ -1102,7 +1213,7 @@ public void executesQueryWithNegatedRegexCorrectly() { template.save(second); Query query = query(where("field").not().regex("Matthews")); - System.out.println(query.getQueryObject()); + List result = template.find(query, Sample.class); assertThat(result.size(), is(1)); assertThat(result.get(0).field, is("Beauford")); @@ -1125,6 +1236,68 @@ public void storesAndRemovesTypeWithComplexId() { template.remove(query(where("id").is(id)), TypeWithMyId.class); } + /** + * @see DATAMONGO-506 + */ + @Test + public void exceutesBasicQueryCorrectly() { + + Address address = new Address(); + address.state = "PA"; + address.city = "Philadelphia"; + + MyPerson person = new MyPerson(); + person.name = "Oleg"; + person.address = address; + + template.save(person); + + Query query = new BasicQuery("{'name' : 'Oleg'}"); + List result = template.find(query, MyPerson.class); + + assertThat(result, hasSize(1)); + assertThat(result.get(0), hasProperty("name", is("Oleg"))); + + query = new BasicQuery("{'address.state' : 'PA' }"); + result = template.find(query, MyPerson.class); + + assertThat(result, hasSize(1)); + assertThat(result.get(0), hasProperty("name", is("Oleg"))); + } + + @Test(expected = OptimisticLockingFailureException.class) + public void optimisticLockingHandling() { + + //Init version + PersonWithVersionPropertyOfTypeInteger person = new PersonWithVersionPropertyOfTypeInteger(); + person.setAge(29); + person.setFirstName("Patryk"); + template.save(person); + + List result = template.findAll(PersonWithVersionPropertyOfTypeInteger.class); + + assertThat(result, hasSize(1)); + assertThat(result.get(0), hasProperty("version", is(0))); + + //Version change + person = result.get(0); + person.setFirstName("Patryk2"); + + template.save(person); + + result = mappingTemplate.findAll(PersonWithVersionPropertyOfTypeInteger.class); + + assertThat(result, hasSize(1)); + assertThat(result.get(0), hasProperty("version", is(1))); + + //Optimistic lock exception + person.setVersion(0); + person.setFirstName("Patryk3"); + + template.save(person); + + } + static class MyId { String first; @@ -1171,4 +1344,21 @@ public DateTime convert(Date source) { return source == null ? null : new DateTime(source.getTime()); } } + + public static class MyPerson { + + String id; + String name; + Address address; + + public String getName() { + return name; + } + } + + static class Address { + + String state; + String city; + } } diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/MongoTemplateUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/MongoTemplateUnitTests.java index 59db409389..b972c24e02 100644 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/MongoTemplateUnitTests.java +++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/MongoTemplateUnitTests.java @@ -22,6 +22,7 @@ import java.math.BigInteger; import java.util.Collections; +import java.util.regex.Pattern; import org.bson.types.ObjectId; import org.junit.Before; @@ -172,6 +173,31 @@ public void convertsUpdateConstraintsUsingConverters() { verify(collection, times(1)).update(Mockito.any(DBObject.class), eq(reference), anyBoolean(), anyBoolean()); } + /** + * @see DATAMONGO-474 + */ + @Test + public void setsUnpopulatedIdField() { + + NotAutogenerateableId entity = new NotAutogenerateableId(); + + template.populateIdIfNecessary(entity, 5); + assertThat(entity.id, is(5)); + } + + /** + * @see DATAMONGO-474 + */ + @Test + public void doesNotSetAlreadyPopulatedId() { + + NotAutogenerateableId entity = new NotAutogenerateableId(); + entity.id = 5; + + template.populateIdIfNecessary(entity, 7); + assertThat(entity.id, is(5)); + } + class AutogenerateableId { @Id @@ -182,6 +208,10 @@ class NotAutogenerateableId { @Id Integer id; + + public Pattern getId() { + return Pattern.compile("."); + } } enum MyConverter implements Converter { diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/PersonWithVersionPropertyOfTypeInteger.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/PersonWithVersionPropertyOfTypeInteger.java new file mode 100644 index 0000000000..64bbcf6a79 --- /dev/null +++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/PersonWithVersionPropertyOfTypeInteger.java @@ -0,0 +1,68 @@ +/* + * Copyright 2010-2011 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.data.mongodb.core; + +import org.springframework.data.mongodb.core.mapping.Version; + +public class PersonWithVersionPropertyOfTypeInteger { + + private String id; + + private String firstName; + + private int age; + + @Version + private Integer version; + + public void setId(String id) { + this.id = id; + } + + public String getId() { + return id; + } + + public String getFirstName() { + return firstName; + } + + public void setFirstName(String firstName) { + this.firstName = firstName; + } + + public int getAge() { + return age; + } + + public void setAge(int age) { + this.age = age; + } + + public Integer getVersion() { + return version; + } + + public void setVersion(Integer version) { + this.version = version; + } + + @Override + public String toString() { + return "PersonWithVersionPropertyOfTypeInteger [id=" + id + ", firstName=" + firstName + ", age=" + age + ", version="+ version + "]"; + } + +} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/TestMongoConfiguration.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/TestMongoConfiguration.java index 2f393b1183..0701556fa8 100644 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/TestMongoConfiguration.java +++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/TestMongoConfiguration.java @@ -21,7 +21,7 @@ public String getDatabaseName() { @Override @Bean public Mongo mongo() throws Exception { - return new Mongo("localhost", 27017); + return new Mongo("127.0.0.1", 27017); } @Override diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/convert/MappingMongoConverterUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/convert/MappingMongoConverterUnitTests.java index 05c3ccced1..ef8eb2077b 100644 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/convert/MappingMongoConverterUnitTests.java +++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/convert/MappingMongoConverterUnitTests.java @@ -49,6 +49,7 @@ import org.springframework.core.convert.converter.Converter; import org.springframework.data.annotation.Id; import org.springframework.data.annotation.PersistenceConstructor; +import org.springframework.data.annotation.TypeAlias; import org.springframework.data.mapping.PropertyPath; import org.springframework.data.mapping.model.MappingException; import org.springframework.data.mapping.model.MappingInstantiationException; @@ -363,7 +364,6 @@ public void readsCollectionWithInterfaceCorrectly() { Contact contact = result.contacts.get(0); assertThat(contact, is(instanceOf(Person.class))); assertThat(((Person) contact).firstname, is("Oliver")); - } @Test @@ -1196,6 +1196,75 @@ public void readsURLFromStringOutOfTheBox() throws Exception { assertThat(result.url, is(new URL("http://springsource.org"))); } + /** + * @see DATAMONGO-485 + */ + @Test + public void writesComplexIdCorrectly() { + + ComplexId id = new ComplexId(); + id.innerId = 4711L; + + ClassWithComplexId entity = new ClassWithComplexId(); + entity.complexId = id; + + DBObject dbObject = new BasicDBObject(); + converter.write(entity, dbObject); + + Object idField = dbObject.get("_id"); + assertThat(idField, is(notNullValue())); + assertThat(idField, is(instanceOf(DBObject.class))); + assertThat(((DBObject) idField).get("innerId"), is((Object) 4711L)); + } + + /** + * @see DATAMONGO-485 + */ + @Test + public void readsComplexIdCorrectly() { + + DBObject innerId = new BasicDBObject("innerId", 4711L); + DBObject entity = new BasicDBObject("_id", innerId); + + ClassWithComplexId result = converter.read(ClassWithComplexId.class, entity); + + assertThat(result.complexId, is(notNullValue())); + assertThat(result.complexId.innerId, is(4711L)); + } + + /** + * @see DATAMONGO-489 + */ + @Test + public void readsArraysAsMapValuesCorrectly() { + + BasicDBList list = new BasicDBList(); + list.add("Foo"); + list.add("Bar"); + + DBObject map = new BasicDBObject("key", list); + DBObject wrapper = new BasicDBObject("mapOfStrings", map); + + ClassWithMapProperty result = converter.read(ClassWithMapProperty.class, wrapper); + assertThat(result.mapOfStrings, is(notNullValue())); + + String[] values = result.mapOfStrings.get("key"); + assertThat(values, is(notNullValue())); + assertThat(values, is(arrayWithSize(2))); + } + + /** + * @see DATAMONGO-497 + */ + @Test + public void readsEmptyCollectionIntoConstructorCorrectly() { + + DBObject source = new BasicDBObject("attributes", new BasicDBList()); + + TypWithCollectionConstructor result = converter.read(TypWithCollectionConstructor.class, source); + assertThat(result.attributes, is(notNullValue())); + } + private static void assertSyntheticFieldValueOf(Object target, Object expected) { for (int i = 0; i < 10; i++) { @@ -1210,6 +1279,37 @@ private static void assertSyntheticFieldValueOf(Object target, Object expected) fail(String.format("Didn't find synthetic field on %s!", target)); } + /** + * @see DATAMGONGO-508 + */ + @Test + public void eagerlyReturnsDBRefObjectIfTargetAlreadyIsOne() { + + DB db = mock(DB.class); + DBRef dbRef = new DBRef(db, "collection", "id"); + + org.springframework.data.mongodb.core.mapping.DBRef annotation = mock(org.springframework.data.mongodb.core.mapping.DBRef.class); + + assertThat(converter.createDBRef(dbRef, annotation), is(dbRef)); + } + + /** + * @see DATAMONGO-523 + */ + @Test + public void considersTypeAliasAnnotation() { + + Aliased aliased = new Aliased(); + aliased.name = "foo"; + + DBObject result = new BasicDBObject(); + converter.write(aliased, result); + + Object type = result.get("_class"); + assertThat(type, is(notNullValue())); + assertThat(type.toString(), is("_")); + } + static class GenericType { T content; } @@ -1275,6 +1375,7 @@ static class ClassWithMapProperty { Map map; Map> mapOfLists; Map mapOfObjects; + Map mapOfStrings; } static class ClassWithNestedMaps { @@ -1373,6 +1474,30 @@ static class URLWrapper { URL url; } + static class ClassWithComplexId { + + @Id + ComplexId complexId; + } + + static class ComplexId { + Long innerId; + } + + static class TypWithCollectionConstructor { + + List attributes; + + public TypWithCollectionConstructor(List attributes) { + this.attributes = attributes; + } + } + + @TypeAlias("_") + static class Aliased { + String name; + } + private class LocalDateToDateConverter implements Converter { public Date convert(LocalDate source) { diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/convert/QueryMapperUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/convert/QueryMapperUnitTests.java index 4c635e3051..7478c276db 100644 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/convert/QueryMapperUnitTests.java +++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/convert/QueryMapperUnitTests.java @@ -1,11 +1,11 @@ /* - * Copyright (c) 2011 by the original author(s). + * Copyright 2011-2012 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, @@ -17,11 +17,14 @@ import static org.hamcrest.Matchers.*; import static org.junit.Assert.*; +import static org.springframework.data.mongodb.core.DBObjectUtils.*; import static org.springframework.data.mongodb.core.query.Criteria.*; import static org.springframework.data.mongodb.core.query.Query.*; import java.math.BigInteger; +import java.util.ArrayList; import java.util.Arrays; +import java.util.List; import org.bson.types.ObjectId; import org.junit.Before; @@ -31,7 +34,10 @@ import org.mockito.runners.MockitoJUnitRunner; import org.springframework.data.annotation.Id; import org.springframework.data.mongodb.MongoDbFactory; +import org.springframework.data.mongodb.core.DBObjectUtils; import org.springframework.data.mongodb.core.Person; +import org.springframework.data.mongodb.core.mapping.DBRef; +import org.springframework.data.mongodb.core.mapping.Field; import org.springframework.data.mongodb.core.mapping.MongoMappingContext; import org.springframework.data.mongodb.core.mapping.MongoPersistentEntity; import org.springframework.data.mongodb.core.query.BasicQuery; @@ -49,11 +55,11 @@ * @author Oliver Gierke */ @RunWith(MockitoJUnitRunner.class) -@SuppressWarnings("unused") public class QueryMapperUnitTests { QueryMapper mapper; MongoMappingContext context; + MappingMongoConverter converter; @Mock MongoDbFactory factory; @@ -63,7 +69,7 @@ public void setUp() { context = new MongoMappingContext(); - MappingMongoConverter converter = new MappingMongoConverter(factory, context); + converter = new MappingMongoConverter(factory, context); converter.afterPropertiesSet(); mapper = new QueryMapper(converter); @@ -200,7 +206,7 @@ public void transformsArraysCorrectly() { } @Test - public void doesNotHandleNestedFieldsWithDefaultIdNames() { + public void doesHandleNestedFieldsWithDefaultIdNames() { BasicDBObject dbObject = new BasicDBObject("id", new ObjectId().toString()); dbObject.put("nested", new BasicDBObject("id", new ObjectId().toString())); @@ -209,7 +215,124 @@ public void doesNotHandleNestedFieldsWithDefaultIdNames() { DBObject result = mapper.getMappedObject(dbObject, entity); assertThat(result.get("_id"), is(instanceOf(ObjectId.class))); - assertThat(((DBObject) result.get("nested")).get("id"), is(instanceOf(String.class))); + assertThat(((DBObject) result.get("nested")).get("_id"), is(instanceOf(ObjectId.class))); + } + + /** + * @see DATAMONGO-493 + */ + @Test + public void doesNotTranslateNonIdPropertiesFor$NeCriteria() { + + ObjectId accidentallyAnObjectId = new ObjectId(); + + Query query = Query.query(Criteria.where("id").is("id_value").and("publishers") + .ne(accidentallyAnObjectId.toString())); + + DBObject dbObject = mapper.getMappedObject(query.getQueryObject(), context.getPersistentEntity(UserEntity.class)); + assertThat(dbObject.get("publishers"), is(instanceOf(DBObject.class))); + + DBObject publishers = (DBObject) dbObject.get("publishers"); + assertThat(publishers.containsField("$ne"), is(true)); + assertThat(publishers.get("$ne"), is(instanceOf(String.class))); + } + + /** + * @see DATAMONGO-494 + */ + @Test + public void usesEntityMetadataInOr() { + + Query query = query(new Criteria().orOperator(where("foo").is("bar"))); + DBObject result = mapper.getMappedObject(query.getQueryObject(), context.getPersistentEntity(Sample.class)); + + assertThat(result.keySet(), hasSize(1)); + assertThat(result.keySet(), hasItem("$or")); + + BasicDBList ors = getAsDBList(result, "$or"); + assertThat(ors, hasSize(1)); + DBObject criterias = getAsDBObject(ors, 0); + assertThat(criterias.keySet(), hasSize(1)); + assertThat(criterias.get("_id"), is(notNullValue())); + assertThat(criterias.get("foo"), is(nullValue())); + } + + @Test + public void translatesPropertyReferenceCorrectly() { + + Query query = query(where("field").is(new CustomizedField())); + DBObject result = mapper + .getMappedObject(query.getQueryObject(), context.getPersistentEntity(CustomizedField.class)); + + assertThat(result.containsField("foo"), is(true)); + assertThat(result.keySet().size(), is(1)); + } + + @Test + public void translatesNestedPropertyReferenceCorrectly() { + + Query query = query(where("field.field").is(new CustomizedField())); + DBObject result = mapper + .getMappedObject(query.getQueryObject(), context.getPersistentEntity(CustomizedField.class)); + + assertThat(result.containsField("foo.foo"), is(true)); + assertThat(result.keySet().size(), is(1)); + } + + @Test + public void returnsOriginalKeyIfNoPropertyReference() { + + Query query = query(where("bar").is(new CustomizedField())); + DBObject result = mapper + .getMappedObject(query.getQueryObject(), context.getPersistentEntity(CustomizedField.class)); + + assertThat(result.containsField("bar"), is(true)); + assertThat(result.keySet().size(), is(1)); + } + + @Test + public void convertsAssociationCorrectly() { + + Reference reference = new Reference(); + reference.id = 5L; + + Query query = query(where("reference").is(reference)); + DBObject object = mapper.getMappedObject(query.getQueryObject(), context.getPersistentEntity(WithDBRef.class)); + + Object referenceObject = object.get("reference"); + + assertThat(referenceObject, is(instanceOf(com.mongodb.DBRef.class))); + } + + @Test + public void convertsNestedAssociationCorrectly() { + + Reference reference = new Reference(); + reference.id = 5L; + + Query query = query(where("withDbRef.reference").is(reference)); + DBObject object = mapper.getMappedObject(query.getQueryObject(), + context.getPersistentEntity(WithDBRefWrapper.class)); + + Object referenceObject = object.get("withDbRef.reference"); + + assertThat(referenceObject, is(instanceOf(com.mongodb.DBRef.class))); + } + + @Test + public void convertsInKeywordCorrectly() { + + Reference first = new Reference(); + first.id = 5L; + + Reference second = new Reference(); + second.id = 6L; + + Query query = query(where("reference").in(first, second)); + DBObject result = mapper.getMappedObject(query.getQueryObject(), context.getPersistentEntity(WithDBRef.class)); + + DBObject reference = DBObjectUtils.getAsDBObject(result, "reference"); + assertThat(reference.containsField("$in"), is(true)); } class IdWrapper { @@ -237,4 +360,31 @@ class BigIntegerId { enum Enum { INSTANCE; } + + class UserEntity { + String id; + List publishers = new ArrayList(); + } + + class CustomizedField { + + @Field("foo") + CustomizedField field; + } + + class WithDBRef { + + @DBRef + Reference reference; + } + + class Reference { + + Long id; + } + + class WithDBRefWrapper { + + WithDBRef withDbRef; + } } diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/index/MongoPersistentEntityIndexCreatorIntegrationTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/index/MongoPersistentEntityIndexCreatorIntegrationTests.java new file mode 100644 index 0000000000..fd468fda5c --- /dev/null +++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/index/MongoPersistentEntityIndexCreatorIntegrationTests.java @@ -0,0 +1,66 @@ +/* + * Copyright 2012 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.data.mongodb.core.index; + +import static org.hamcrest.Matchers.*; +import static org.junit.Assert.*; + +import java.util.List; + +import org.hamcrest.Matchers; +import org.junit.After; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.data.mongodb.core.MongoOperations; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; + +/** + * Integration tests for {@link MongoPersistentEntityIndexCreator}. + * + * @author Oliver Gierke + */ +@RunWith(SpringJUnit4ClassRunner.class) +@ContextConfiguration +public class MongoPersistentEntityIndexCreatorIntegrationTests { + + @Autowired + @Qualifier("mongo1") + MongoOperations templateOne; + + @Autowired + @Qualifier("mongo2") + MongoOperations templateTwo; + + @After + public void cleanUp() { + templateOne.dropCollection(SampleEntity.class); + templateTwo.dropCollection(SampleEntity.class); + } + + @Test + public void foo() { + + List indexInfo = templateOne.indexOps(SampleEntity.class).getIndexInfo(); + assertThat(indexInfo, hasSize(greaterThan(0))); + assertThat(indexInfo, Matchers. hasItem(hasProperty("name", is("prop")))); + + indexInfo = templateTwo.indexOps(SampleEntity.class).getIndexInfo(); + assertThat(indexInfo, hasSize(0)); + } +} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/index/MongoPersistentEntityIndexCreatorUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/index/MongoPersistentEntityIndexCreatorUnitTests.java new file mode 100644 index 0000000000..f47eddd445 --- /dev/null +++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/index/MongoPersistentEntityIndexCreatorUnitTests.java @@ -0,0 +1,108 @@ +/* + * Copyright 2012 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.data.mongodb.core.index; + +import static org.hamcrest.CoreMatchers.*; +import static org.junit.Assert.*; + +import java.util.Collections; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.runners.MockitoJUnitRunner; +import org.springframework.context.ApplicationContext; +import org.springframework.data.mapping.context.MappingContextEvent; +import org.springframework.data.mongodb.MongoDbFactory; +import org.springframework.data.mongodb.core.mapping.Field; +import org.springframework.data.mongodb.core.mapping.MongoMappingContext; +import org.springframework.data.mongodb.core.mapping.MongoPersistentEntity; +import org.springframework.data.mongodb.core.mapping.MongoPersistentProperty; + +import com.mongodb.DBObject; + +/** + * Unit tests for {@link MongoPersistentEntityIndexCreator}. + * + * @author Oliver Gierke + */ +@RunWith(MockitoJUnitRunner.class) +public class MongoPersistentEntityIndexCreatorUnitTests { + + @Mock + MongoDbFactory factory; + @Mock + ApplicationContext context; + + @Test + public void buildsIndexDefinitionUsingFieldName() { + + MongoMappingContext mappingContext = new MongoMappingContext(); + mappingContext.setInitialEntitySet(Collections.singleton(Person.class)); + mappingContext.initialize(); + + DummyMongoPersistentEntityIndexCreator creator = new DummyMongoPersistentEntityIndexCreator(mappingContext, factory); + + assertThat(creator.indexDefinition, is(notNullValue())); + assertThat(creator.indexDefinition.keySet(), hasItem("fieldname")); + assertThat(creator.name, is("indexName")); + } + + @Test + public void doesNotCreateIndexForEntityComingFromDifferentMappingContext() { + + MongoMappingContext mappingContext = new MongoMappingContext(); + + MongoMappingContext personMappingContext = new MongoMappingContext(); + personMappingContext.setInitialEntitySet(Collections.singleton(Person.class)); + personMappingContext.initialize(); + + DummyMongoPersistentEntityIndexCreator creator = new DummyMongoPersistentEntityIndexCreator(mappingContext, factory); + + MongoPersistentEntity entity = personMappingContext.getPersistentEntity(Person.class); + MappingContextEvent, MongoPersistentProperty> event = new MappingContextEvent, MongoPersistentProperty>( + personMappingContext, entity); + + creator.onApplicationEvent(event); + + assertThat(creator.indexDefinition, is(nullValue())); + } + + static class Person { + + @Indexed(name = "indexName") + @Field("fieldname") + String field; + } + + static class DummyMongoPersistentEntityIndexCreator extends MongoPersistentEntityIndexCreator { + + DBObject indexDefinition; + String name; + + public DummyMongoPersistentEntityIndexCreator(MongoMappingContext mappingContext, MongoDbFactory mongoDbFactory) { + super(mappingContext, mongoDbFactory); + } + + @Override + protected void ensureIndex(String collection, String name, DBObject indexDefinition, boolean unique, + boolean dropDups, boolean sparse) { + + this.name = name; + this.indexDefinition = indexDefinition; + } + } +} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/index/SampleEntity.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/index/SampleEntity.java new file mode 100644 index 0000000000..a099c4cb08 --- /dev/null +++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/index/SampleEntity.java @@ -0,0 +1,14 @@ +package org.springframework.data.mongodb.core.index; + +import org.springframework.data.annotation.Id; +import org.springframework.data.mongodb.core.mapping.Document; + +@Document +public class SampleEntity { + + @Id + String id; + + @Indexed + String prop; +} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/mapping/MongoMappingContextUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/mapping/MongoMappingContextUnitTests.java index ea53a2808e..16f3c7d5f2 100644 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/mapping/MongoMappingContextUnitTests.java +++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/mapping/MongoMappingContextUnitTests.java @@ -16,6 +16,9 @@ package org.springframework.data.mongodb.core.mapping; +import static org.hamcrest.CoreMatchers.*; +import static org.junit.Assert.*; + import java.util.Collections; import java.util.Map; @@ -23,6 +26,8 @@ import org.springframework.data.annotation.Id; import org.springframework.data.mapping.model.MappingException; +import com.mongodb.DBRef; + /** * Unit tests for {@link MongoMappingContext}. * @@ -46,6 +51,13 @@ public void rejectsEntityWithMultipleIdProperties() { context.getPersistentEntity(ClassWithMultipleIdProperties.class); } + @Test + public void doesNotReturnPersistentEntityForMongoSimpleType() { + + MongoMappingContext context = new MongoMappingContext(); + assertThat(context.getPersistentEntity(DBRef.class), is(nullValue())); + } + class ClassWithMultipleIdProperties { @Id diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/query/CriteriaTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/query/CriteriaTests.java index 2b141a5e5f..fac13a5d6b 100644 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/query/CriteriaTests.java +++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/query/CriteriaTests.java @@ -15,11 +15,11 @@ */ package org.springframework.data.mongodb.core.query; -import static org.junit.Assert.*; import static org.hamcrest.CoreMatchers.*; +import static org.junit.Assert.*; + import org.junit.Test; import org.springframework.data.mongodb.InvalidMongoDbApiUsageException; -import org.springframework.data.mongodb.core.query.Criteria; import com.mongodb.BasicDBObject; import com.mongodb.DBObject; @@ -58,4 +58,14 @@ public void testCriteriaWithMultipleConditionsForSameKey() { Criteria c = new Criteria("name").gte("M").and("name").ne("A"); c.getCriteriaObject(); } + + @Test + public void equalIfCriteriaMatches() { + + Criteria left = new Criteria("name").is("Foo").and("lastname").is("Bar"); + Criteria right = new Criteria("name").is("Bar").and("lastname").is("Bar"); + + assertThat(left, is(not(right))); + assertThat(right, is(not(left))); + } } diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/AbstractPersonRepositoryIntegrationTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/AbstractPersonRepositoryIntegrationTests.java index 7bc53f6853..99cd7bc6de 100644 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/AbstractPersonRepositoryIntegrationTests.java +++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/AbstractPersonRepositoryIntegrationTests.java @@ -495,4 +495,32 @@ public void bindsDateParameterForManuallyDefinedQueryCorrectly() { List result = repository.findByCreatedAtLessThanManually(boyd.createdAt); assertThat(result.isEmpty(), is(false)); } -} \ No newline at end of file + + /** + * @see DATAMONGO-472 + */ + @Test + public void findsPeopleUsingNotPredicate() { + + List result = repository.findByLastnameNot("Matthews"); + assertThat(result, not(hasItem(dave))); + assertThat(result, hasSize(5)); + } + + /** + * @see DATAMONGO-521 + */ + @Test + public void executesAndQueryCorrectly() { + + List result = repository.findByFirstnameAndLastname("Dave", "Matthews"); + + assertThat(result, hasSize(1)); + assertThat(result, hasItem(dave)); + + result = repository.findByFirstnameAndLastname("Oliver August", "Matthews"); + + assertThat(result, hasSize(1)); + assertThat(result, hasItem(oliver)); + } +} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/PersonRepository.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/PersonRepository.java index 66667ca167..bf35a99b6b 100644 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/PersonRepository.java +++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/PersonRepository.java @@ -106,6 +106,8 @@ public interface PersonRepository extends MongoRepository, Query */ List findByFirstnameNotIn(Collection firstnames); + List findByFirstnameAndLastname(String firstname, String lastname); + /** * Returns all {@link Person}s with an age between the two given values. * @@ -183,4 +185,11 @@ public interface PersonRepository extends MongoRepository, Query */ List findByCreatedAtAfter(Date date); + /** + * @see DATAMONGO-472 + * @param lastname + * @return + */ + List findByLastnameNot(String lastname); + } diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/PersonRepositoryIntegrationTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/PersonRepositoryIntegrationTests.java index 45ebae97e7..51196811ec 100644 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/PersonRepositoryIntegrationTests.java +++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/PersonRepositoryIntegrationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2010 the original author or authors. + * Copyright 2010-2012 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/cdi/CdiExtensionIntegrationTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/cdi/CdiExtensionIntegrationTests.java index fd72704646..5f05ccac60 100644 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/cdi/CdiExtensionIntegrationTests.java +++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/cdi/CdiExtensionIntegrationTests.java @@ -20,6 +20,7 @@ import org.apache.webbeans.cditest.CdiTestContainer; import org.apache.webbeans.cditest.CdiTestContainerLoader; +import org.junit.AfterClass; import org.junit.BeforeClass; import org.junit.Test; import org.springframework.data.mongodb.repository.Person; @@ -39,11 +40,16 @@ public static void setUp() throws Exception { container.bootContainer(); } + @AfterClass + public static void tearDown() throws Exception { + container.shutdownContainer(); + } + @Test public void bootstrapsRepositoryCorrectly() { RepositoryClient client = container.getInstance(RepositoryClient.class); - PersonRepository repository = client.getRepository(); + CdiPersonRepository repository = client.getRepository(); assertThat(repository, is(notNullValue())); diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/cdi/PersonRepository.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/cdi/CdiPersonRepository.java similarity index 92% rename from spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/cdi/PersonRepository.java rename to spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/cdi/CdiPersonRepository.java index f86c063975..48b1cebb11 100644 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/cdi/PersonRepository.java +++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/cdi/CdiPersonRepository.java @@ -18,7 +18,7 @@ import org.springframework.data.mongodb.repository.Person; import org.springframework.data.repository.Repository; -public interface PersonRepository extends Repository { +public interface CdiPersonRepository extends Repository { void deleteAll(); diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/cdi/RepositoryClient.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/cdi/RepositoryClient.java index 34d681bcbd..f8fd1755c9 100644 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/cdi/RepositoryClient.java +++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/cdi/RepositoryClient.java @@ -18,18 +18,17 @@ import javax.inject.Inject; /** - * * @author Oliver Gierke */ class RepositoryClient { @Inject - PersonRepository repository; + CdiPersonRepository repository; /** * @return the repository */ - public PersonRepository getRepository() { + public CdiPersonRepository getRepository() { return repository; } } diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/config/MongoRepositoriesRegistrarIntegrationTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/config/MongoRepositoriesRegistrarIntegrationTests.java new file mode 100644 index 0000000000..26989e144d --- /dev/null +++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/config/MongoRepositoriesRegistrarIntegrationTests.java @@ -0,0 +1,58 @@ +/* + * Copyright 2012 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.data.mongodb.repository.config; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.data.mongodb.core.MongoOperations; +import org.springframework.data.mongodb.core.MongoTemplate; +import org.springframework.data.mongodb.core.SimpleMongoDbFactory; +import org.springframework.data.mongodb.repository.PersonRepository; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; + +import com.mongodb.Mongo; + +/** + * Integration tests for {@link MongoRepositoriesRegistrar}. + * + * @author Oliver Gierke + */ +@RunWith(SpringJUnit4ClassRunner.class) +@ContextConfiguration +public class MongoRepositoriesRegistrarIntegrationTests { + + @Configuration + @EnableMongoRepositories(basePackages = "org.springframework.data.mongodb.repository") + static class Config { + + @Bean + public MongoOperations mongoTemplate() throws Exception { + return new MongoTemplate(new SimpleMongoDbFactory(new Mongo(), "database")); + } + } + + @Autowired + PersonRepository personRepository; + + @Test + public void testConfiguration() { + + } +} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/query/ConvertingParameterAccessorUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/query/ConvertingParameterAccessorUnitTests.java index 6b70a23c6f..10f40c2d2f 100644 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/query/ConvertingParameterAccessorUnitTests.java +++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/query/ConvertingParameterAccessorUnitTests.java @@ -15,11 +15,13 @@ */ package org.springframework.data.mongodb.repository.query; -import static org.mockito.Mockito.*; +import static org.hamcrest.Matchers.*; import static org.junit.Assert.*; -import static org.hamcrest.CoreMatchers.*; +import static org.mockito.Mockito.*; import java.util.Arrays; +import java.util.Collection; + import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -27,7 +29,11 @@ import org.mockito.runners.MockitoJUnitRunner; import org.springframework.data.mongodb.MongoDbFactory; import org.springframework.data.mongodb.core.convert.MappingMongoConverter; +import org.springframework.data.mongodb.core.mapping.DBRef; import org.springframework.data.mongodb.core.mapping.MongoMappingContext; +import org.springframework.data.mongodb.core.mapping.MongoPersistentEntity; +import org.springframework.data.mongodb.core.mapping.MongoPersistentProperty; +import org.springframework.data.mongodb.repository.query.ConvertingParameterAccessor.PotentiallyConvertingIterator; import com.mongodb.BasicDBList; @@ -76,4 +82,66 @@ public void convertsCollectionUponAccess() { assertThat(result, is((Object) reference)); } + + /** + * @see DATAMONGO-505 + */ + @Test + public void convertsAssociationsToDBRef() { + + Property property = new Property(); + property.id = 5L; + + Object result = setupAndConvert(property); + + assertThat(result, is(instanceOf(com.mongodb.DBRef.class))); + com.mongodb.DBRef dbRef = (com.mongodb.DBRef) result; + assertThat(dbRef.getRef(), is("property")); + assertThat(dbRef.getId(), is((Object) 5L)); + } + + /** + * @see DATAMONGO-505 + */ + @Test + public void convertsAssociationsToDBRefForCollections() { + + Property property = new Property(); + property.id = 5L; + + Object result = setupAndConvert(Arrays.asList(property)); + + assertThat(result, is(instanceOf(Collection.class))); + Collection collection = (Collection) result; + + assertThat(collection, hasSize(1)); + Object element = collection.iterator().next(); + + assertThat(element, is(instanceOf(com.mongodb.DBRef.class))); + com.mongodb.DBRef dbRef = (com.mongodb.DBRef) element; + assertThat(dbRef.getRef(), is("property")); + assertThat(dbRef.getId(), is((Object) 5L)); + } + + private Object setupAndConvert(Object... parameters) { + + MongoParameterAccessor delegate = new StubParameterAccessor(parameters); + PotentiallyConvertingIterator iterator = new ConvertingParameterAccessor(converter, delegate).iterator(); + + MongoPersistentEntity entity = context.getPersistentEntity(Entity.class); + MongoPersistentProperty property = entity.getPersistentProperty("property"); + + return iterator.nextConverted(property); + } + + static class Entity { + + @DBRef + Property property; + } + + static class Property { + + Long id; + } } diff --git a/spring-data-mongodb/src/test/resources/geospatial.xml b/spring-data-mongodb/src/test/resources/geospatial.xml index dcb331dc9f..13a01ac350 100644 --- a/spring-data-mongodb/src/test/resources/geospatial.xml +++ b/spring-data-mongodb/src/test/resources/geospatial.xml @@ -3,7 +3,7 @@ xmlns:mongo="http://www.springframework.org/schema/data/mongo" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd - http://www.springframework.org/schema/data/mongo http://www.springframework.org/schema/data/mongo/spring-mongo-1.0.xsd"> + http://www.springframework.org/schema/data/mongo http://www.springframework.org/schema/data/mongo/spring-mongo.xsd"> diff --git a/spring-data-mongodb/src/test/resources/mapping.xml b/spring-data-mongodb/src/test/resources/mapping.xml index 6b45093c28..42c4c6799f 100644 --- a/spring-data-mongodb/src/test/resources/mapping.xml +++ b/spring-data-mongodb/src/test/resources/mapping.xml @@ -3,7 +3,7 @@ xmlns:mongo="http://www.springframework.org/schema/data/mongo" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd - http://www.springframework.org/schema/data/mongo http://www.springframework.org/schema/data/mongo/spring-mongo-1.0.xsd"> + http://www.springframework.org/schema/data/mongo http://www.springframework.org/schema/data/mongo/spring-mongo.xsd"> diff --git a/spring-data-mongodb/src/test/resources/namespace/db-factory-bean-custom-write-concern.xml b/spring-data-mongodb/src/test/resources/namespace/db-factory-bean-custom-write-concern.xml index c42ab81a09..ef7b48fbea 100644 --- a/spring-data-mongodb/src/test/resources/namespace/db-factory-bean-custom-write-concern.xml +++ b/spring-data-mongodb/src/test/resources/namespace/db-factory-bean-custom-write-concern.xml @@ -2,7 +2,7 @@ diff --git a/spring-data-mongodb/src/test/resources/namespace/db-factory-bean.xml b/spring-data-mongodb/src/test/resources/namespace/db-factory-bean.xml index ea91e67f7a..bce7b0aaff 100644 --- a/spring-data-mongodb/src/test/resources/namespace/db-factory-bean.xml +++ b/spring-data-mongodb/src/test/resources/namespace/db-factory-bean.xml @@ -2,7 +2,7 @@ diff --git a/spring-data-mongodb/src/test/resources/org/springframework/data/mongodb/config/MongoNamespaceReplicaSetTests-context.xml b/spring-data-mongodb/src/test/resources/org/springframework/data/mongodb/config/MongoNamespaceReplicaSetTests-context.xml index dd1890419e..28003d77bd 100644 --- a/spring-data-mongodb/src/test/resources/org/springframework/data/mongodb/config/MongoNamespaceReplicaSetTests-context.xml +++ b/spring-data-mongodb/src/test/resources/org/springframework/data/mongodb/config/MongoNamespaceReplicaSetTests-context.xml @@ -3,7 +3,7 @@ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:mongo="http://www.springframework.org/schema/data/mongo" xmlns:context="http://www.springframework.org/schema/context" - xsi:schemaLocation="http://www.springframework.org/schema/data/mongo http://www.springframework.org/schema/data/mongo/spring-mongo-1.0.xsd + xsi:schemaLocation="http://www.springframework.org/schema/data/mongo http://www.springframework.org/schema/data/mongo/spring-mongo.xsd http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd"> diff --git a/spring-data-mongodb/src/test/resources/org/springframework/data/mongodb/config/MongoNamespaceTests-context.xml b/spring-data-mongodb/src/test/resources/org/springframework/data/mongodb/config/MongoNamespaceTests-context.xml index 339d06f504..7a855c5956 100644 --- a/spring-data-mongodb/src/test/resources/org/springframework/data/mongodb/config/MongoNamespaceTests-context.xml +++ b/spring-data-mongodb/src/test/resources/org/springframework/data/mongodb/config/MongoNamespaceTests-context.xml @@ -3,7 +3,7 @@ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:mongo="http://www.springframework.org/schema/data/mongo" xmlns:context="http://www.springframework.org/schema/context" - xsi:schemaLocation="http://www.springframework.org/schema/data/mongo http://www.springframework.org/schema/data/mongo/spring-mongo-1.0.xsd + xsi:schemaLocation="http://www.springframework.org/schema/data/mongo http://www.springframework.org/schema/data/mongo/spring-mongo.xsd http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd"> diff --git a/spring-data-mongodb/src/test/resources/org/springframework/data/mongodb/core/index/MongoPersistentEntityIndexCreatorIntegrationTests-context.xml b/spring-data-mongodb/src/test/resources/org/springframework/data/mongodb/core/index/MongoPersistentEntityIndexCreatorIntegrationTests-context.xml new file mode 100644 index 0000000000..6ed28955f5 --- /dev/null +++ b/spring-data-mongodb/src/test/resources/org/springframework/data/mongodb/core/index/MongoPersistentEntityIndexCreatorIntegrationTests-context.xml @@ -0,0 +1,23 @@ + + + + + + + + + + + + + + + + + + diff --git a/spring-data-mongodb/src/test/resources/org/springframework/data/mongodb/repository/PersonRepositoryIntegrationTests-context.xml b/spring-data-mongodb/src/test/resources/org/springframework/data/mongodb/repository/PersonRepositoryIntegrationTests-context.xml index a1bffb752c..06032c18d1 100644 --- a/spring-data-mongodb/src/test/resources/org/springframework/data/mongodb/repository/PersonRepositoryIntegrationTests-context.xml +++ b/spring-data-mongodb/src/test/resources/org/springframework/data/mongodb/repository/PersonRepositoryIntegrationTests-context.xml @@ -3,9 +3,9 @@ xmlns:mongo="http://www.springframework.org/schema/data/mongo" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:util="http://www.springframework.org/schema/util" - xsi:schemaLocation="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 - http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-3.0.xsd"> + xsi:schemaLocation="http://www.springframework.org/schema/data/mongo http://www.springframework.org/schema/data/mongo/spring-mongo.xsd + http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd + http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd"> diff --git a/spring-data-mongodb/src/test/resources/org/springframework/data/mongodb/repository/RepositoryIndexCreationIntegrationTests-context.xml b/spring-data-mongodb/src/test/resources/org/springframework/data/mongodb/repository/RepositoryIndexCreationIntegrationTests-context.xml index be635304fe..d994229bac 100644 --- a/spring-data-mongodb/src/test/resources/org/springframework/data/mongodb/repository/RepositoryIndexCreationIntegrationTests-context.xml +++ b/spring-data-mongodb/src/test/resources/org/springframework/data/mongodb/repository/RepositoryIndexCreationIntegrationTests-context.xml @@ -3,9 +3,9 @@ xmlns:mongo="http://www.springframework.org/schema/data/mongo" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:util="http://www.springframework.org/schema/util" - xsi:schemaLocation="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 - http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-3.0.xsd"> + xsi:schemaLocation="http://www.springframework.org/schema/data/mongo http://www.springframework.org/schema/data/mongo/spring-mongo.xsd + http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd + http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd"> @@ -15,7 +15,6 @@ - + diff --git a/spring-data-mongodb/src/test/resources/org/springframework/data/mongodb/repository/config/MongoNamespaceIntegrationTests-context.xml b/spring-data-mongodb/src/test/resources/org/springframework/data/mongodb/repository/config/MongoNamespaceIntegrationTests-context.xml index 2496a94302..b80e84c56e 100644 --- a/spring-data-mongodb/src/test/resources/org/springframework/data/mongodb/repository/config/MongoNamespaceIntegrationTests-context.xml +++ b/spring-data-mongodb/src/test/resources/org/springframework/data/mongodb/repository/config/MongoNamespaceIntegrationTests-context.xml @@ -4,7 +4,7 @@ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:repository="http://www.springframework.org/schema/data/repository" xmlns:util="http://www.springframework.org/schema/util" - xsi:schemaLocation="http://www.springframework.org/schema/data/mongo http://www.springframework.org/schema/data/mongo/spring-mongo-1.0.xsd + xsi:schemaLocation="http://www.springframework.org/schema/data/mongo http://www.springframework.org/schema/data/mongo/spring-mongo.xsd http://www.springframework.org/schema/data/repository http://www.springframework.org/schema/data/repository/spring-repository-1.0.xsd http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-3.0.xsd"> diff --git a/spring-data-mongodb/src/test/resources/server-jmx.xml b/spring-data-mongodb/src/test/resources/server-jmx.xml index 2456cf7e39..7b24ed7aba 100644 --- a/spring-data-mongodb/src/test/resources/server-jmx.xml +++ b/spring-data-mongodb/src/test/resources/server-jmx.xml @@ -4,7 +4,7 @@ xmlns:p="http://www.springframework.org/schema/p" xmlns:mongo="http://www.springframework.org/schema/data/mongo" xmlns:context="http://www.springframework.org/schema/context" - xsi:schemaLocation="http://www.springframework.org/schema/data/mongo http://www.springframework.org/schema/data/mongo/spring-mongo-1.0.xsd + xsi:schemaLocation="http://www.springframework.org/schema/data/mongo http://www.springframework.org/schema/data/mongo/spring-mongo.xsd http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"> diff --git a/spring-data-mongodb/src/test/resources/template-mapping.xml b/spring-data-mongodb/src/test/resources/template-mapping.xml index 61a20872d3..001d3f4fa5 100644 --- a/spring-data-mongodb/src/test/resources/template-mapping.xml +++ b/spring-data-mongodb/src/test/resources/template-mapping.xml @@ -2,8 +2,8 @@ + xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd + http://www.springframework.org/schema/data/mongo http://www.springframework.org/schema/data/mongo/spring-mongo.xsd"> diff --git a/spring-data-mongodb/template.mf b/spring-data-mongodb/template.mf index a1e8257bd4..9d9222fa88 100644 --- a/spring-data-mongodb/template.mf +++ b/spring-data-mongodb/template.mf @@ -7,7 +7,7 @@ Import-Package: Export-Template: org.springframework.data.mongodb.*;version="${project.version}" Import-Template: - com.google.common.base.*;version="[11.0.0,12.0.0)";resolution:=optional, + com.google.common.base.*;version="[11.0.0,14.0.0)";resolution:=optional, com.mongodb.*;version="0", com.mysema.query.*;version="[2.1.1, 3.0.0)";resolution:=optional, javax.annotation.processing.*;version="0", diff --git a/src/docbkx/index.xml b/src/docbkx/index.xml index 5290dd587f..03d6d63e4e 100644 --- a/src/docbkx/index.xml +++ b/src/docbkx/index.xml @@ -52,7 +52,7 @@ - + @@ -72,9 +72,12 @@ Appendix - + + + + diff --git a/src/docbkx/reference/mongo-repositories.xml b/src/docbkx/reference/mongo-repositories.xml index 9289838d6b..58d2b72311 100644 --- a/src/docbkx/reference/mongo-repositories.xml +++ b/src/docbkx/reference/mongo-repositories.xml @@ -9,8 +9,8 @@ This chapter will point out the specialties for repository support for MongoDB. This builds on the core repository support explained in . So make sure you've got a sound understanding - of the basic concepts explained there. + linkend="repositories"/>. So make sure you've got a sound understanding of + the basic concepts explained there.
      @@ -91,6 +91,36 @@ configure mongo-template-ref explicitly if you deviate from this convention. + If you'd rather like to go with JavaConfig use the + @EnableMongoRepositories annotation. The + annotation carries the very same attributes like the namespace element. If + no base package is configured the infrastructure will scan the package of + the annotated configuration class. + + + JavaConfig for repositories + + @Configuration +@EnableMongoRepositories +class ApplicationConfig extends AbstractMongoConfiguration { + + @Override + protected String getDatabaseName() { + return "e-store"; + } + + @Override + public Mongo mongo() throws Exception { + return new Mongo(); + } + + @Override + protected String getMappingBasePackage() { + return "com.oreilly.springdata.mongodb" + } +} + + As our domain repository extends PagingAndSortingRepository it provides you with CRUD operations as well as methods for paginated and sorted access to @@ -169,11 +199,11 @@ public class PersonRepositoryTests { Supported keywords for query methods - + - + - + @@ -370,7 +400,7 @@ Distance distance = new Distance(200, Metrics.KILOMETERS); Geo-near queries - + public interface PersonRepository extends MongoRepository<Person, String> @@ -512,4 +542,4 @@ Page<Person> page = repository.findAll(person.lastname.contains("a"), MongoDB queries.
      - \ No newline at end of file + diff --git a/src/docbkx/reference/mongodb.xml b/src/docbkx/reference/mongodb.xml index e0285d037d..3228426c7e 100644 --- a/src/docbkx/reference/mongodb.xml +++ b/src/docbkx/reference/mongodb.xml @@ -2135,7 +2135,7 @@ MapReduceResults<ValueObject> results = mongoOperations.mapReduce(query, "
      Group Operations - As an alternative to usiing Map-Reduce to perform data aggregation, + As an alternative to using Map-Reduce to perform data aggregation, you can use the group operation which feels similar to using SQL's group by query style, diff --git a/src/main/resources/changelog.txt b/src/main/resources/changelog.txt index d4d701139d..848224f052 100644 --- a/src/main/resources/changelog.txt +++ b/src/main/resources/changelog.txt @@ -1,6 +1,135 @@ Spring Data MongoDB Changelog ============================================= +Changes in version 1.1.0.RC1 (2012-24-08) +----------------------------------------- +** Bug + * [DATAMONGO-493] - Criteria.ne() method converts all value into ObjectId + * [DATAMONGO-494] - $or/$nor expressions do not consider entity class mapping + * [DATAMONGO-495] - JSON can't serialize Enum when printing Query in DEBUG message + * [DATAMONGO-497] - Reading an empty List throws a MappingInstantiationException because it returns an HashSet instead of returning an ArrayList + * [DATAMONGO-505] - Conversion of associations doesn't work for collection values + * [DATAMONGO-508] - DBRef can accidentally get added as PersistentProperty + * [DATAMONGO-517] - QueryMapping incorrectly translates complex keywords + +** Improvement + * [DATAMONGO-496] - AbstractMongoConfiguration.getMappingBasePackage() could default to config class' package + * [DATAMONGO-499] - Namespace XSDs of current release version should refer to repositories XSD in version 1.0 + * [DATAMONGO-500] - Index creation reacts on events not intended for it + * [DATAMONGO-502] - QueryMapper should transparently translate property names to field names + * [DATAMONGO-509] - SimpleMongoRepository.exists(…) can be improved. + * [DATAMONGO-510] - Criteria should only use BasicDBList internally + * [DATAMONGO-511] - QueryMapper should correctly transform associations + * [DATAMONGO-516] - Make Spring 3.1.2.RELEASE default Spring dependency version + +** Task + * [DATAMONGO-513] - Release 1.1 RC1 + + +Changes in version 1.0.4.RELEASE MongoDB (2012-08-24) +----------------------------------------------------- +** Bug + * [DATAMONGO-493] - Criteria.ne() method converts all value into ObjectId + * [DATAMONGO-494] - $or/$nor expressions do not consider entity class mapping + * [DATAMONGO-495] - JSON can't serialize Enum when printing Query in DEBUG message + +** Improvement + * [DATAMONGO-499] - Namespace XSDs of current release version should refer to repositories XSD in version 1.0 + +** Task + * [DATAMONGO-514] - Release 1.0.4. + +Changes in version 1.1.0.M2 (2012-24-07) +---------------------------------------- +** Bug + * [DATAMONGO-378] - MapReduceResults ClassCastException due to raw results counts as Long + * [DATAMONGO-424] - Declaring a list of DBRef in a domian class results in Null for each DBRef when reading from mongo database + * [DATAMONGO-425] - Binding a Date to a manually defined repository query fails + * [DATAMONGO-428] - ClassCastException when using outputDatabase option in map-reduce + * [DATAMONGO-446] - Pageable query methods returning List are broken + * [DATAMONGO-447] - Removal of Documents fails in in debug mode for Documents with complex ids + * [DATAMONGO-450] - enabling DEBUG causes RuntimeException + * [DATAMONGO-454] - ServerAddressPropertyEditor fails if a hostname is unresolvable + * [DATAMONGO-458] - When reading back empty collections unmodifiable instances of Collections.emptyList/Set is returned. + * [DATAMONGO-462] - findAll() fails with NPE - discovering the root cause + * [DATAMONGO-465] - Mongo inserts document with "_id" as an integer but saves with "_id" as a string. + * [DATAMONGO-467] - String @id field is not mapped to ObjectId when using QueryDSL ".id" path + * [DATAMONGO-469] - Query creation from method names using AND criteria does not work anymore + * [DATAMONGO-474] - Wrong property is used for Id mapping + * [DATAMONGO-475] - 'group' operation fails where query references non primitive property + * [DATAMONGO-480] - The WriteResultChecking is not used in case of insert or save of documents. + * [DATAMONGO-483] - @Indexed(unique=true, name="foo") puts name's value to the 'key' in the MongoDB + * [DATAMONGO-489] - ClassCastException when loading Map + +** Improvement + * [DATAMONGO-448] - Remove the need for Converters for complex classes that are used as IDs + * [DATAMONGO-455] - Document how to use raw queries using BasicQuery + * [DATAMONGO-460] - Improve Querydsl implementation internals + * [DATAMONGO-466] - QueryMapper shouldn't map id properties of nested classes + * [DATAMONGO-470] - Criteria and Query should have proper equals(…) and hashCode() method. + * [DATAMONGO-477] - Change upper bound of Google Guava package import to 13 + * [DATAMONGO-482] - typo in documentation - 2 i's in usiing + * [DATAMONGO-486] - Polish namspace implementation + * [DATAMONGO-491] - Release 1.1.0.M2 + +** New Feature + * [DATAMONGO-476] - JavaConfig support for Mongo repositories + +** Task + * [DATAMONGO-451] - Tweak pom.xml to let Sonar build run without Bundlor + * [DATAMONGO-490] - Fix minor typos + + +Changes in version 1.0.3.RELEASE (2012-24-07) +--------------------------------------------- +** Bug + * [DATAMONGO-467] - String @id field is not mapped to ObjectId when using QueryDSL ".id" path + * [DATAMONGO-469] - Query creation from method names using AND criteria does not work anymore + * [DATAMONGO-474] - Wrong property is used for Id mapping + * [DATAMONGO-475] - 'group' operation fails where query references non primitive property + * [DATAMONGO-480] - The WriteResultChecking is not used in case of insert or save of documents. + * [DATAMONGO-483] - @Indexed(unique=true, name="foo") puts name's value to the 'key' in the MongoDB + * [DATAMONGO-489] - ClassCastException when loading Map + +** Improvement + * [DATAMONGO-466] - QueryMapper shouldn't map id properties of nested classes + * [DATAMONGO-470] - Criteria and Query should have proper equals(…) and hashCode() method. + * [DATAMONGO-482] - typo in documentation - 2 i's in usiing + +** Task + * [DATAMONGO-492] - Release 1.0.3 + + +Changes in version 1.0.2.RELEASE (2012-06-20) +--------------------------------------------- +** Bug + * [DATAMONGO-360] - java.lang.ClassCastException when placing GeospatialIndex into IndexOperations and invoking IndexOperations.getIndexInfo() + * [DATAMONGO-366] - Chapter 3.2. points to wrong bugtracker + * [DATAMONGO-378] - MapReduceResults ClassCastException due to raw results counts as Long + * [DATAMONGO-382] - ClassCastException: "com.mongodb.BasicDBObject cannot be cast to com.mongodb.BasicDBList" during find() + * [DATAMONGO-411] - Potential ClassCastExceptions in MongoPersistentEntityIndexCreator + * [DATAMONGO-412] - getUserCredentials() is called twice in AbstractMongoConfiguration::mongoDbFactory() + * [DATAMONGO-413] - Using "Or" in repository query yields a ClassCastException + * [DATAMONGO-422] - UUIDToBinaryConverter not compatible with mongo java driver + * [DATAMONGO-423] - Criteria.regex should use java.util.Pattern instead of $regex + * [DATAMONGO-425] - Binding a Date to a manually defined repository query fails + * [DATAMONGO-428] - ClassCastException when using outputDatabase option in map-reduce + * [DATAMONGO-429] - using @Query annotation, arrays are translated somewhere between query creation and mongo interpretation + * [DATAMONGO-446] - Pageable query methods returning List are broken + * [DATAMONGO-447] - Removal of Documents fails in in debug mode for Documents with complex ids + * [DATAMONGO-450] - enabling DEBUG causes RuntimeException + * [DATAMONGO-454] - ServerAddressPropertyEditor fails if a hostname is unresolvable + * [DATAMONGO-461] - MappedConstructor potentially throws NullPointerException + * [DATAMONGO-462] - findAll() fails with NPE - discovering the root cause + +** Improvement + * [DATAMONGO-448] - Remove the need for Converters for complex classes that are used as IDs + * [DATAMONGO-455] - Document how to use raw queries using BasicQuery + +** Task + * [DATAMONGO-463] - Release 1.0.2 + + Changes in version 1.1.0.M1 (2012-05-07) ----------------------------------------