diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/ReactiveMongoDatabaseFactory.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/ReactiveMongoDatabaseFactory.java index 52e80b953..e67f46084 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/ReactiveMongoDatabaseFactory.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/ReactiveMongoDatabaseFactory.java @@ -24,7 +24,7 @@ import com.mongodb.reactivestreams.client.MongoDatabase; /** * Interface for factories creating reactive {@link MongoDatabase} instances. - * + * * @author Mark Paluch * @since 2.0 */ @@ -32,7 +32,7 @@ public interface ReactiveMongoDatabaseFactory { /** * Creates a default {@link MongoDatabase} instance. - * + * * @return * @throws DataAccessException */ @@ -40,7 +40,7 @@ public interface ReactiveMongoDatabaseFactory { /** * Creates a {@link MongoDatabase} instance to access the database with the given name. - * + * * @param dbName must not be {@literal null} or empty. * @return * @throws DataAccessException @@ -49,7 +49,7 @@ public interface ReactiveMongoDatabaseFactory { /** * Exposes a shared {@link MongoExceptionTranslator}. - * + * * @return will never be {@literal null}. */ PersistenceExceptionTranslator getExceptionTranslator(); diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/config/AbstractReactiveMongoConfiguration.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/config/AbstractReactiveMongoConfiguration.java index 3468a5ce9..c05ffb4fc 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/config/AbstractReactiveMongoConfiguration.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/config/AbstractReactiveMongoConfiguration.java @@ -18,7 +18,9 @@ package org.springframework.data.mongodb.config; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import org.springframework.data.mongodb.ReactiveMongoDatabaseFactory; import org.springframework.data.mongodb.core.MongoTemplate; +import org.springframework.data.mongodb.core.ReactiveMongoOperations; import org.springframework.data.mongodb.core.SimpleReactiveMongoDatabaseFactory; import org.springframework.data.mongodb.core.ReactiveMongoTemplate; import org.springframework.data.mongodb.core.SimpleMongoDbFactory; @@ -28,7 +30,7 @@ import com.mongodb.reactivestreams.client.MongoClient; /** * Base class for reactive Spring Data MongoDB configuration using JavaConfig. - * + * * @author Mark Paluch * @since 2.0 * @see MongoConfigurationSupport @@ -39,39 +41,39 @@ public abstract class AbstractReactiveMongoConfiguration extends MongoConfigurat /** * Return the {@link MongoClient} instance to connect to. Annotate with {@link Bean} in case you want to expose a * {@link MongoClient} instance to the {@link org.springframework.context.ApplicationContext}. - * + * * @return */ public abstract MongoClient mongoClient(); /** * Creates a {@link ReactiveMongoTemplate}. - * + * * @return */ @Bean - public ReactiveMongoTemplate reactiveMongoTemplate() throws Exception { + public ReactiveMongoOperations reactiveMongoTemplate() throws Exception { return new ReactiveMongoTemplate(mongoDbFactory(), mappingMongoConverter()); } /** * Creates a {@link SimpleMongoDbFactory} to be used by the {@link MongoTemplate}. Will use the {@link Mongo} instance * configured in {@link #mongoClient()}. - * + * * @see #mongoClient() * @see #reactiveMongoTemplate() * @return * @throws Exception */ @Bean - public SimpleReactiveMongoDatabaseFactory mongoDbFactory() { + public ReactiveMongoDatabaseFactory mongoDbFactory() { return new SimpleReactiveMongoDatabaseFactory(mongoClient(), getDatabaseName()); } /** * Creates a {@link MappingMongoConverter} using the configured {@link #mongoDbFactory()} and * {@link #mongoMappingContext()}. Will get {@link #customConversions()} applied. - * + * * @see #customConversions() * @see #mongoMappingContext() * @see #mongoDbFactory() 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 0c2000291..fb1381e16 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 @@ -1,5 +1,5 @@ /* - * Copyright 2011-2014 the original author or authors. + * Copyright 2011-2016 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. @@ -73,6 +73,7 @@ import org.w3c.dom.Element; * @author Maciej Walkowiak * @author Thomas Darimont * @author Christoph Strobl + * @author Mark Paluch */ public class MappingMongoConverterParser implements BeanDefinitionParser { @@ -121,6 +122,7 @@ public class MappingMongoConverterParser implements BeanDefinitionParser { } if(!registry.containsBeanDefinition("indexOperationsProvider")){ + BeanDefinitionBuilder indexOperationsProviderBuilder = BeanDefinitionBuilder.genericBeanDefinition("org.springframework.data.mongodb.core.DefaultIndexOperationsProvider"); indexOperationsProviderBuilder.addConstructorArgReference(dbFactoryRef); parserContext.registerBeanComponent(new BeanComponentDefinition(indexOperationsProviderBuilder.getBeanDefinition(), "indexOperationsProvider")); @@ -129,9 +131,7 @@ public class MappingMongoConverterParser implements BeanDefinitionParser { try { registry.getBeanDefinition(INDEX_HELPER_BEAN_NAME); } catch (NoSuchBeanDefinitionException ignored) { - if (!StringUtils.hasText(dbFactoryRef)) { - dbFactoryRef = DB_FACTORY_BEAN_NAME; - } + BeanDefinitionBuilder indexHelperBuilder = BeanDefinitionBuilder .genericBeanDefinition(MongoPersistentEntityIndexCreator.class); indexHelperBuilder.addConstructorArgReference(ctxRef); diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/config/MongoConfigurationSupport.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/config/MongoConfigurationSupport.java index 7fb731ce5..b948bdfdc 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/config/MongoConfigurationSupport.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/config/MongoConfigurationSupport.java @@ -16,6 +16,7 @@ package org.springframework.data.mongodb.config; +import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.HashSet; @@ -28,7 +29,9 @@ import org.springframework.core.convert.converter.Converter; import org.springframework.core.type.filter.AnnotationTypeFilter; import org.springframework.data.annotation.Persistent; import org.springframework.data.authentication.UserCredentials; +import org.springframework.data.mapping.context.MappingContext; import org.springframework.data.mapping.context.MappingContextIsNewStrategyFactory; +import org.springframework.data.mapping.context.PersistentEntities; import org.springframework.data.mapping.model.CamelCaseAbbreviatingFieldNamingStrategy; import org.springframework.data.mapping.model.FieldNamingStrategy; import org.springframework.data.mapping.model.PropertyNameFieldNamingStrategy; @@ -51,7 +54,7 @@ import com.mongodb.MongoClient; /** * Base class for Spring Data MongoDB to be extended for JavaConfiguration usage. - * + * * @author Mark Paluch * @since 2.0 */ @@ -100,20 +103,22 @@ public abstract class MongoConfigurationSupport { /** * Returns a {@link MappingContextIsNewStrategyFactory} wrapped into a {@link CachingIsNewStrategyFactory}. - * + * * @return * @throws ClassNotFoundException */ @Bean public IsNewStrategyFactory isNewStrategyFactory() throws ClassNotFoundException { - return new CachingIsNewStrategyFactory(new MappingContextIsNewStrategyFactory(mongoMappingContext())); + + return new CachingIsNewStrategyFactory(new MappingContextIsNewStrategyFactory( + new PersistentEntities(Arrays.> asList(new MappingContext[] { mongoMappingContext() })))); } /** * Register custom {@link Converter}s in a {@link CustomConversions} object if required. These * {@link CustomConversions} will be registered with the {@link #mappingMongoConverter()} and * {@link #mongoMappingContext()}. Returns an empty {@link CustomConversions} instance by default. - * + * * @return must not be {@literal null}. */ @Bean @@ -124,7 +129,7 @@ public abstract class MongoConfigurationSupport { /** * Scans the mapping base package for classes annotated with {@link Document}. By default, it scans for entities in * all packages returned by {@link #getMappingBasePackages()}. - * + * * @see #getMappingBasePackages() * @return * @throws ClassNotFoundException @@ -143,7 +148,7 @@ public abstract class MongoConfigurationSupport { /** * Scans the given base package for entities, i.e. MongoDB specific types annotated with {@link Document} and * {@link Persistent}. - * + * * @param basePackage must not be {@literal null}. * @return * @throws ClassNotFoundException @@ -178,7 +183,7 @@ public abstract class MongoConfigurationSupport { * Configures whether to abbreviate field names for domain objects by configuring a * {@link CamelCaseAbbreviatingFieldNamingStrategy} on the {@link MongoMappingContext} instance created. For advanced * customization needs, consider overriding {@link #mappingMongoConverter()}. - * + * * @return */ protected boolean abbreviateFieldNames() { @@ -187,7 +192,7 @@ public abstract class MongoConfigurationSupport { /** * Configures a {@link FieldNamingStrategy} on the {@link MongoMappingContext} instance created. - * + * * @return * @since 1.5 */ diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/DefaultIndexOperations.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/DefaultIndexOperations.java index 5ccdf3822..2f3f8f4c3 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/DefaultIndexOperations.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/DefaultIndexOperations.java @@ -15,7 +15,7 @@ */ package org.springframework.data.mongodb.core; -import static org.springframework.data.mongodb.core.MongoTemplate.potentiallyConvertRuntimeException; +import static org.springframework.data.mongodb.core.MongoTemplate.*; import java.util.ArrayList; import java.util.List; @@ -65,19 +65,18 @@ public class DefaultIndexOperations implements IndexOperations { * (non-Javadoc) * @see org.springframework.data.mongodb.core.IndexOperations#ensureIndex(org.springframework.data.mongodb.core.index.IndexDefinition) */ - public void ensureIndex(final IndexDefinition indexDefinition) { - execute(collection -> { + public String ensureIndex(final IndexDefinition indexDefinition) { + + return execute(collection -> { Document indexOptions = indexDefinition.getIndexOptions(); if (indexOptions != null) { - IndexOptions ops = IndexConverters.DEFINITION_TO_MONGO_INDEX_OPTIONS.convert(indexDefinition); - collection.createIndex(indexDefinition.getIndexKeys(), ops); - } else { - collection.createIndex(indexDefinition.getIndexKeys()); + IndexOptions ops = IndexConverters.indexDefinitionToIndexOptionsConverter().convert(indexDefinition); + return collection.createIndex(indexDefinition.getIndexKeys(), ops); } - return null; + return collection.createIndex(indexDefinition.getIndexKeys()); }); } @@ -86,11 +85,10 @@ public class DefaultIndexOperations implements IndexOperations { * @see org.springframework.data.mongodb.core.IndexOperations#dropIndex(java.lang.String) */ public void dropIndex(final String name) { - execute(new CollectionCallback() { - public Void doInCollection(MongoCollection collection) throws MongoException, DataAccessException { - collection.dropIndex(name); - return null; - } + + execute(collection -> { + collection.dropIndex(name); + return null; }); } @@ -110,6 +108,7 @@ public class DefaultIndexOperations implements IndexOperations { public List getIndexInfo() { return execute(new CollectionCallback>() { + public List doInCollection(MongoCollection collection) throws MongoException, DataAccessException { @@ -124,7 +123,7 @@ public class DefaultIndexOperations implements IndexOperations { while (cursor.hasNext()) { Document ix = cursor.next(); - IndexInfo indexInfo = IndexConverters.DOCUMENT_INDEX_INFO.convert(ix); + IndexInfo indexInfo = IndexConverters.documentToIndexInfoConverter().convert(ix); indexInfoList.add(indexInfo); } diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/DefaultReactiveIndexOperations.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/DefaultReactiveIndexOperations.java index adb168ecb..4b5c4e43d 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/DefaultReactiveIndexOperations.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/DefaultReactiveIndexOperations.java @@ -62,7 +62,7 @@ public class DefaultReactiveIndexOperations implements ReactiveIndexOperations { if (indexOptions != null) { return collection.createIndex(indexDefinition.getIndexKeys(), - IndexConverters.DEFINITION_TO_MONGO_INDEX_OPTIONS.convert(indexDefinition)); + IndexConverters.indexDefinitionToIndexOptionsConverter().convert(indexDefinition)); } return collection.createIndex(indexDefinition.getIndexKeys()); @@ -96,7 +96,7 @@ public class DefaultReactiveIndexOperations implements ReactiveIndexOperations { ListIndexesPublisher indexesPublisher = collection.listIndexes(Document.class); - return Flux.from(indexesPublisher).map(t -> IndexConverters.DOCUMENT_INDEX_INFO.convert(t)); + return Flux.from(indexesPublisher).map(IndexConverters.documentToIndexInfoConverter()::convert); }); } } diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/IndexConverters.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/IndexConverters.java index 44ea31941..40ba7738e 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/IndexConverters.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/IndexConverters.java @@ -35,14 +35,15 @@ import com.mongodb.client.model.IndexOptions; /** * {@link Converter Converters} for index-related MongoDB documents/types. - * + * * @author Mark Paluch + * @author Christoph Strobl * @since 2.0 */ abstract class IndexConverters { - public final static Converter DEFINITION_TO_MONGO_INDEX_OPTIONS; - public final static Converter DOCUMENT_INDEX_INFO; + private static final Converter DEFINITION_TO_MONGO_INDEX_OPTIONS; + private static final Converter DOCUMENT_INDEX_INFO; private static final Double ONE = Double.valueOf(1); private static final Double MINUS_ONE = Double.valueOf(-1); @@ -58,6 +59,14 @@ abstract class IndexConverters { } + static Converter indexDefinitionToIndexOptionsConverter() { + return DEFINITION_TO_MONGO_INDEX_OPTIONS; + } + + static Converter documentToIndexInfoConverter() { + return DOCUMENT_INDEX_INFO; + } + private static Converter getIndexDefinitionIndexOptionsConverter() { return indexDefinition -> { @@ -115,14 +124,14 @@ abstract class IndexConverters { private static Converter getDocumentIndexInfoConverter() { return ix -> { - Document keyDbObject = (Document) ix.get("key"); - int numberOfElements = keyDbObject.keySet().size(); + Document keyDocument = (Document) ix.get("key"); + int numberOfElements = keyDocument.keySet().size(); List indexFields = new ArrayList(numberOfElements); - for (String key : keyDbObject.keySet()) { + for (String key : keyDocument.keySet()) { - Object value = keyDbObject.get(key); + Object value = keyDocument.get(key); if (TWO_D_IDENTIFIERS.contains(value)) { indexFields.add(IndexField.geo(key)); @@ -148,11 +157,10 @@ abstract class IndexConverters { String name = ix.get("name").toString(); boolean unique = ix.containsKey("unique") ? (Boolean) ix.get("unique") : false; - boolean dropDuplicates = ix.containsKey("dropDups") ? (Boolean) ix.get("dropDups") : false; boolean sparse = ix.containsKey("sparse") ? (Boolean) ix.get("sparse") : false; String language = ix.containsKey("default_language") ? (String) ix.get("default_language") : ""; - return new IndexInfo(indexFields, name, unique, dropDuplicates, sparse, language); + return new IndexInfo(indexFields, name, unique, sparse, language); }; } } diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/IndexOperations.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/IndexOperations.java index 3ea8cb6e6..986314703 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/IndexOperations.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/IndexOperations.java @@ -35,7 +35,7 @@ public interface IndexOperations { * * @param indexDefinition must not be {@literal null}. */ - void ensureIndex(IndexDefinition indexDefinition); + String ensureIndex(IndexDefinition indexDefinition); /** * Drops an index from this collection. diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/IndexOperationsAdapter.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/IndexOperationsAdapter.java new file mode 100644 index 000000000..e941f39cf --- /dev/null +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/IndexOperationsAdapter.java @@ -0,0 +1,66 @@ +/* + * Copyright 2016. 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 java.util.List; + +import org.springframework.data.mongodb.core.index.IndexDefinition; +import org.springframework.data.mongodb.core.index.IndexInfo; +import org.springframework.util.Assert; + +/** + * Adapter for creating synchronous {@link IndexOperations}. + * + * @author Christoph Strobl + * @since 2.0 + */ +public interface IndexOperationsAdapter extends IndexOperations { + + /** + * Obtain a blocking variant of {@link IndexOperations} wrapping {@link ReactiveIndexOperations}. + * + * @param reactiveIndexOperations must not be {@literal null}. + * @return never {@literal null} + */ + static IndexOperationsAdapter blocking(ReactiveIndexOperations reactiveIndexOperations) { + + Assert.notNull(reactiveIndexOperations, "ReactiveIndexOperations must not be null!"); + + return new IndexOperationsAdapter() { + + @Override + public String ensureIndex(IndexDefinition indexDefinition) { + return reactiveIndexOperations.ensureIndex(indexDefinition).block(); + } + + @Override + public void dropIndex(String name) { + reactiveIndexOperations.dropIndex(name).block(); + } + + @Override + public void dropAllIndexes() { + reactiveIndexOperations.dropAllIndexes().block(); + } + + @Override + public List getIndexInfo() { + return reactiveIndexOperations.getIndexInfo().collectList().block(); + } + }; + } +} diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/IndexOperationsProvider.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/IndexOperationsProvider.java index de2102ede..50ed704eb 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/IndexOperationsProvider.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/IndexOperationsProvider.java @@ -16,11 +16,10 @@ package org.springframework.data.mongodb.core; -import org.springframework.dao.support.PersistenceExceptionTranslator; - /** * TODO: Revisit for a better pattern. * @author Mark Paluch + * @since 2.0 */ public interface IndexOperationsProvider { diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ReactiveCollectionCallback.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ReactiveCollectionCallback.java index 9e5d876fc..edbbc23fc 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ReactiveCollectionCallback.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ReactiveCollectionCallback.java @@ -22,6 +22,11 @@ import com.mongodb.reactivestreams.client.MongoCollection; import org.bson.Document; import org.reactivestreams.Publisher; +/** + * @author Mark Paluch + * @param + * @since 2.0 + */ public interface ReactiveCollectionCallback { Publisher doInCollection(MongoCollection collection) throws MongoException, DataAccessException; diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ReactiveDatabaseCallback.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ReactiveDatabaseCallback.java index 32198bdbb..5c6d1f955 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ReactiveDatabaseCallback.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ReactiveDatabaseCallback.java @@ -21,6 +21,11 @@ import com.mongodb.MongoException; import com.mongodb.reactivestreams.client.MongoDatabase; import org.reactivestreams.Publisher; +/** + * @author Mark Paluch + * @param + * @since 2.0 + */ public interface ReactiveDatabaseCallback { Publisher doInDB(MongoDatabase db) throws MongoException, DataAccessException; diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ReactiveIndexOperations.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ReactiveIndexOperations.java index 45646526d..88c89d2c3 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ReactiveIndexOperations.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ReactiveIndexOperations.java @@ -15,6 +15,8 @@ */ package org.springframework.data.mongodb.core; +import java.util.List; + import org.springframework.data.mongodb.core.index.IndexDefinition; import org.springframework.data.mongodb.core.index.IndexInfo; @@ -25,7 +27,8 @@ import reactor.core.publisher.Mono; * Index operations on a collection. * * @author Mark Paluch - * @since 1.11 + * @author Christoph Strobl + * @since 2.0 */ public interface ReactiveIndexOperations { @@ -55,4 +58,5 @@ public interface ReactiveIndexOperations { * @return index information on the collection */ Flux getIndexInfo(); + } diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ReactiveMongoClientFactoryBean.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ReactiveMongoClientFactoryBean.java index f9cb26f77..1ae18fda9 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ReactiveMongoClientFactoryBean.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ReactiveMongoClientFactoryBean.java @@ -29,6 +29,8 @@ import com.mongodb.reactivestreams.client.MongoClients; * Convenient factory for configuring a reactive streams {@link MongoClient}. * * @author Mark Paluch + * @author Christoph Strobl + * @since 2.0 */ public class ReactiveMongoClientFactoryBean extends AbstractFactoryBean implements PersistenceExceptionTranslator { @@ -122,6 +124,6 @@ public class ReactiveMongoClientFactoryBean extends AbstractFactoryBean { diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ReactiveMongoDatabaseHolder.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ReactiveMongoDatabaseHolder.java deleted file mode 100644 index 5679fa2ef..000000000 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ReactiveMongoDatabaseHolder.java +++ /dev/null @@ -1,88 +0,0 @@ -/* - * Copyright 2016 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 com.mongodb.reactivestreams.client.MongoDatabase; - -import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; - -import org.springframework.transaction.support.ResourceHolderSupport; -import org.springframework.util.Assert; - - -/** - * @author Mark Paluch - */ -class ReactiveMongoDatabaseHolder extends ResourceHolderSupport { - private static final Object DEFAULT_KEY = new Object(); - - private final Map dbMap = new ConcurrentHashMap(); - - public ReactiveMongoDatabaseHolder(MongoDatabase db) { - addMongoDatabase(db); - } - - public ReactiveMongoDatabaseHolder(Object key, MongoDatabase db) { - addMongoDatabase(key, db); - } - - public MongoDatabase getMongoDatabase() { - return getMongoDatabase(DEFAULT_KEY); - } - - public MongoDatabase getMongoDatabase(Object key) { - return this.dbMap.get(key); - } - - public MongoDatabase getAnyMongoDatabase() { - if (!this.dbMap.isEmpty()) { - return this.dbMap.values().iterator().next(); - } - return null; - } - - public void addMongoDatabase(MongoDatabase session) { - addMongoDatabase(DEFAULT_KEY, session); - } - - public void addMongoDatabase(Object key, MongoDatabase session) { - Assert.notNull(key, "Key must not be null"); - Assert.notNull(session, "DB must not be null"); - this.dbMap.put(key, session); - } - - public MongoDatabase removeMongoDatabase(Object key) { - return this.dbMap.remove(key); - } - - public boolean containsMongoDatabase(MongoDatabase session) { - return this.dbMap.containsValue(session); - } - - public boolean isEmpty() { - return this.dbMap.isEmpty(); - } - - public boolean doesNotHoldNonDefaultMongoDatabase() { - synchronized (this.dbMap) { - return this.dbMap.isEmpty() || (this.dbMap.size() == 1 && this.dbMap.containsKey(DEFAULT_KEY)); - } - } - -} diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ReactiveMongoDbUtils.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ReactiveMongoDbUtils.java index f526fb7b4..cb80c494a 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ReactiveMongoDbUtils.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ReactiveMongoDbUtils.java @@ -17,24 +17,17 @@ package org.springframework.data.mongodb.core; import com.mongodb.reactivestreams.client.MongoClient; import com.mongodb.reactivestreams.client.MongoDatabase; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import org.springframework.data.authentication.UserCredentials; -import org.springframework.data.mongodb.util.MongoClientVersion; -import org.springframework.transaction.support.TransactionSynchronizationManager; - /** * Helper class featuring helper methods for internal MongoDb classes. Mainly intended for internal use within the * framework. - * + * * @author Mark Paluch + * @author Christoph Strobl + * @since 2.0 */ public abstract class ReactiveMongoDbUtils { - private static final Logger LOGGER = LoggerFactory.getLogger(ReactiveMongoDbUtils.class); - /** * Private constructor to prevent instantiation. */ @@ -42,109 +35,17 @@ public abstract class ReactiveMongoDbUtils { /** * Obtains a {@link MongoDatabase} connection for the given {@link MongoClient} instance and database name - * + * * @param mongo the {@link MongoClient} instance, must not be {@literal null}. * @param databaseName the database name, must not be {@literal null} or empty. * @return the {@link MongoDatabase} connection */ public static MongoDatabase getMongoDatabase(MongoClient mongo, String databaseName) { - return doGetMongoDatabase(mongo, databaseName, UserCredentials.NO_CREDENTIALS, true, databaseName); + return doGetMongoDatabase(mongo, databaseName, true); } - private static MongoDatabase doGetMongoDatabase(MongoClient mongo, String databaseName, UserCredentials credentials, - boolean allowCreate, String authenticationDatabaseName) { - - ReactiveMongoDatabaseHolder dbHolder = (ReactiveMongoDatabaseHolder) TransactionSynchronizationManager - .getResource(mongo); - - // Do we have a populated holder and TX sync active? - if (dbHolder != null && !dbHolder.isEmpty() && TransactionSynchronizationManager.isSynchronizationActive()) { - - MongoDatabase db = dbHolder.getMongoDatabase(databaseName); - - // DB found but not yet synchronized - if (db != null && !dbHolder.isSynchronizedWithTransaction()) { - - LOGGER.debug("Registering Spring transaction synchronization for existing MongoDB {}.", databaseName); - - TransactionSynchronizationManager.registerSynchronization(new MongoSynchronization(dbHolder, mongo)); - dbHolder.setSynchronizedWithTransaction(true); - } - - if (db != null) { - return db; - } - } - - // Lookup fresh database instance - LOGGER.debug("Getting Mongo Database name=[{}]", databaseName); - - MongoDatabase db = mongo.getDatabase(databaseName); - - // TX sync active, bind new database to thread - if (TransactionSynchronizationManager.isSynchronizationActive()) { - - LOGGER.debug("Registering Spring transaction synchronization for MongoDB instance {}.", databaseName); - - ReactiveMongoDatabaseHolder holderToUse = dbHolder; - - if (holderToUse == null) { - holderToUse = new ReactiveMongoDatabaseHolder(databaseName, db); - } else { - holderToUse.addMongoDatabase(databaseName, db); - } - - // synchronize holder only if not yet synchronized - if (!holderToUse.isSynchronizedWithTransaction()) { - TransactionSynchronizationManager.registerSynchronization(new MongoSynchronization(holderToUse, mongo)); - holderToUse.setSynchronizedWithTransaction(true); - } - - if (holderToUse != dbHolder) { - TransactionSynchronizationManager.bindResource(mongo, holderToUse); - } - } - - // Check whether we are allowed to return the DB. - if (!allowCreate && !isDBTransactional(db, mongo)) { - throw new IllegalStateException( - "No Mongo DB bound to thread, " + "and configuration does not allow creation of non-transactional one here"); - } - - return db; + private static MongoDatabase doGetMongoDatabase(MongoClient mongo, String databaseName, boolean allowCreate) { + return mongo.getDatabase(databaseName); } - /** - * Return whether the given DB instance is transactional, that is, bound to the current thread by Spring's transaction - * facilities. - * - * @param db the DB to check - * @param mongoClient the Mongo instance that the DB was created with (may be null) - * @return whether the DB is transactional - */ - public static boolean isDBTransactional(MongoDatabase db, MongoClient mongoClient) { - - if (mongoClient == null) { - return false; - } - ReactiveMongoDatabaseHolder dbHolder = (ReactiveMongoDatabaseHolder) TransactionSynchronizationManager - .getResource(mongoClient); - return dbHolder != null && dbHolder.containsMongoDatabase(db); - } - - /** - * Check if credentials present. In case we're using a mongo-java-driver version 3 or above we do not have the need - * for authentication as the auth data has to be provided within the MongoClient - * - * @param credentials - * @return - */ - private static boolean requiresAuthDbAuthentication(UserCredentials credentials) { - - if (credentials == null || !credentials.hasUsername()) { - return false; - } - - return !MongoClientVersion.isMongo3Driver(); - } } diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ReactiveMongoOperations.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ReactiveMongoOperations.java index e6ed168a9..d0268ab3f 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ReactiveMongoOperations.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ReactiveMongoOperations.java @@ -21,7 +21,6 @@ import org.bson.Document; import org.reactivestreams.Publisher; import org.reactivestreams.Subscription; import org.springframework.data.geo.GeoResult; -import org.springframework.data.geo.GeoResults; import org.springframework.data.mongodb.core.convert.MappingMongoConverter; import org.springframework.data.mongodb.core.convert.MongoConverter; import org.springframework.data.mongodb.core.query.BasicQuery; @@ -46,6 +45,7 @@ import reactor.core.publisher.Mono; * {@link ReactiveMongoOperations} is deferred until subscriber subscribes to the {@link Publisher}. * * @author Mark Paluch + * @author Christoph Strobl * @see Flux * @see Mono * @see http://projectreactor.io/docs/ @@ -67,20 +67,6 @@ public interface ReactiveMongoOperations { */ ReactiveIndexOperations reactiveIndexOps(Class entityClass); - /** - * Returns the operations that can be performed on indexes - * - * @return index operations on the named collection - */ - IndexOperations indexOps(String collectionName); - - /** - * Returns the operations that can be performed on indexes - * - * @return index operations on the named collection associated with the given entity class - */ - IndexOperations indexOps(Class entityClass); - /** * Execute the a MongoDB command expressed as a JSON string. This will call the method JSON.parse that is part of the * MongoDB driver to convert the JSON string to a DBObject. Any errors that result from executing this command will be @@ -955,4 +941,5 @@ public interface ReactiveMongoOperations { * @return */ MongoConverter getConverter(); + } diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ReactiveMongoTemplate.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ReactiveMongoTemplate.java index fb1230ff4..c4d8c0163 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ReactiveMongoTemplate.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ReactiveMongoTemplate.java @@ -60,7 +60,6 @@ import org.springframework.data.mapping.model.ConvertingPropertyAccessor; import org.springframework.data.mapping.model.MappingException; import org.springframework.data.mongodb.MongoDbFactory; import org.springframework.data.mongodb.ReactiveMongoDatabaseFactory; -import org.springframework.data.mongodb.core.MongoTemplate.DocumentCallback; import org.springframework.data.mongodb.core.convert.DbRefProxyHandler; import org.springframework.data.mongodb.core.convert.DbRefResolver; import org.springframework.data.mongodb.core.convert.DbRefResolverCallback; @@ -69,8 +68,6 @@ import org.springframework.data.mongodb.core.convert.MongoConverter; import org.springframework.data.mongodb.core.convert.MongoWriter; import org.springframework.data.mongodb.core.convert.QueryMapper; import org.springframework.data.mongodb.core.convert.UpdateMapper; -import org.springframework.data.mongodb.core.index.IndexDefinition; -import org.springframework.data.mongodb.core.index.IndexInfo; import org.springframework.data.mongodb.core.index.MongoMappingEventPublisher; import org.springframework.data.mongodb.core.index.MongoPersistentEntityIndexCreator; import org.springframework.data.mongodb.core.mapping.MongoMappingContext; @@ -128,11 +125,12 @@ import reactor.util.function.Tuple2; * extract results. This class executes BSON queries or updates, initiating iteration over {@link FindPublisher} and * catching MongoDB exceptions and translating them to the generic, more informative exception hierarchy defined in the * org.springframework.dao package. Can be used within a service implementation via direct instantiation with a - * {@link SimpleReactiveMongoDatabaseFactory} reference, or get prepared in an application context and given to services as - * bean reference. Note: The {@link SimpleReactiveMongoDatabaseFactory} should always be configured as a bean in the + * {@link SimpleReactiveMongoDatabaseFactory} reference, or get prepared in an application context and given to services + * as bean reference. Note: The {@link SimpleReactiveMongoDatabaseFactory} should always be configured as a bean in the * application context, in the first case given to the service directly, in the second case to the prepared template. * * @author Mark Paluch + * @author Christoph Strobl * @since 2.0 */ public class ReactiveMongoTemplate implements ReactiveMongoOperations, ApplicationContextAware { @@ -211,7 +209,7 @@ public class ReactiveMongoTemplate implements ReactiveMongoOperations, Applicati if (null != mappingContext && mappingContext instanceof MongoMappingContext) { indexCreator = new MongoPersistentEntityIndexCreator((MongoMappingContext) mappingContext, - new BlockingIndexOptionsProvider(this)); + (collectionName) -> IndexOperationsAdapter.blocking(reactiveIndexOps(collectionName))); eventPublisher = new MongoMappingEventPublisher(indexCreator); if (mappingContext instanceof ApplicationEventPublisherAware) { ((ApplicationEventPublisherAware) mappingContext).setApplicationEventPublisher(eventPublisher); @@ -329,20 +327,6 @@ public class ReactiveMongoTemplate implements ReactiveMongoOperations, Applicati return new DefaultReactiveIndexOperations(this, determineCollectionName(entityClass)); } - /* (non-Javadoc) - * @see org.springframework.data.mongodb.core.ReactiveMongoOperations#reactiveIndexOps(java.lang.String) - */ - public IndexOperations indexOps(String collectionName) { - return new BlockingIndexOperations(new DefaultReactiveIndexOperations(this, collectionName)); - } - - /* (non-Javadoc) - * @see org.springframework.data.mongodb.core.ReactiveMongoOperations#reactiveIndexOps(java.lang.Class) - */ - public IndexOperations indexOps(Class entityClass) { - return new BlockingIndexOperations(new DefaultReactiveIndexOperations(this, determineCollectionName(entityClass))); - } - public String getCollectionName(Class entityClass) { return this.determineCollectionName(entityClass); } @@ -480,7 +464,7 @@ public class ReactiveMongoTemplate implements ReactiveMongoOperations, Applicati * @see org.springframework.data.mongodb.core.ReactiveMongoOperations#createCollection(java.lang.Class, org.springframework.data.mongodb.core.CollectionOptions) */ public Mono> createCollection(Class entityClass, - CollectionOptions collectionOptions) { + CollectionOptions collectionOptions) { return createCollection(determineCollectionName(entityClass), collectionOptions); } @@ -495,7 +479,7 @@ public class ReactiveMongoTemplate implements ReactiveMongoOperations, Applicati * @see org.springframework.data.mongodb.core.ReactiveMongoOperations#createCollection(java.lang.String, org.springframework.data.mongodb.core.CollectionOptions) */ public Mono> createCollection(final String collectionName, - final CollectionOptions collectionOptions) { + final CollectionOptions collectionOptions) { return doCreateCollection(collectionName, convertToCreateCollectionOptions(collectionOptions)); } @@ -730,7 +714,7 @@ public class ReactiveMongoTemplate implements ReactiveMongoOperations, Applicati * @see org.springframework.data.mongodb.core.ReactiveMongoOperations#findAndModify(org.springframework.data.mongodb.core.query.Query, org.springframework.data.mongodb.core.query.Update, org.springframework.data.mongodb.core.FindAndModifyOptions, java.lang.Class, java.lang.String) */ public Mono findAndModify(Query query, Update update, FindAndModifyOptions options, Class entityClass, - String collectionName) { + String collectionName) { return doFindAndModify(collectionName, query.getQueryObject(), query.getFieldsObject(), getMappedSortObject(query, entityClass), entityClass, update, options); } @@ -777,7 +761,7 @@ public class ReactiveMongoTemplate implements ReactiveMongoOperations, Applicati final Document Document = query == null ? null : queryMapper.getMappedObject(query.getQueryObject(), - entityClass == null ? null : mappingContext.getPersistentEntity(entityClass)); + entityClass == null ? null : mappingContext.getPersistentEntity(entityClass)); return collection.count(Document); }); @@ -904,7 +888,7 @@ public class ReactiveMongoTemplate implements ReactiveMongoOperations, Applicati } protected Flux doInsertBatch(final String collectionName, final Collection batchToSave, - final MongoWriter writer) { + final MongoWriter writer) { Assert.notNull(writer); @@ -1094,7 +1078,7 @@ public class ReactiveMongoTemplate implements ReactiveMongoOperations, Applicati } private MongoCollection prepareCollection(MongoCollection collection, - WriteConcern writeConcernToUse) { + WriteConcern writeConcernToUse) { MongoCollection collectionToUse = collection; if (writeConcernToUse != null) { @@ -1194,12 +1178,12 @@ public class ReactiveMongoTemplate implements ReactiveMongoOperations, Applicati * @see org.springframework.data.mongodb.core.ReactiveMongoOperations#updateMulti(org.springframework.data.mongodb.core.query.Query, org.springframework.data.mongodb.core.query.Update, java.lang.Class, java.lang.String) */ public Mono updateMulti(final Query query, final Update update, Class entityClass, - String collectionName) { + String collectionName) { return doUpdate(collectionName, query, update, entityClass, false, true); } protected Mono doUpdate(final String collectionName, final Query query, final Update update, - final Class entityClass, final boolean upsert, final boolean multi) { + final Class entityClass, final boolean upsert, final boolean multi) { MongoPersistentEntity entity = entityClass == null ? null : getPersistentEntity(entityClass); @@ -1413,7 +1397,7 @@ public class ReactiveMongoTemplate implements ReactiveMongoOperations, Applicati } protected Mono doRemove(final String collectionName, final Query query, - final Class entityClass) { + final Class entityClass) { if (query == null) { throw new InvalidDataAccessApiUsageException("Query passed in to remove can't be null!"); @@ -1437,7 +1421,7 @@ public class ReactiveMongoTemplate implements ReactiveMongoOperations, Applicati if (LOGGER.isDebugEnabled()) { LOGGER.debug("Remove using query: {} in collection: {}.", - new Object[]{serializeToJsonSafely(dboq), collectionName}); + new Object[] { serializeToJsonSafely(dboq), collectionName }); } return collectionToUse.deleteMany(dboq); @@ -1534,7 +1518,7 @@ public class ReactiveMongoTemplate implements ReactiveMongoOperations, Applicati * @return the collection that was created */ protected Mono> doCreateCollection(final String collectionName, - final CreateCollectionOptions collectionOptions) { + final CreateCollectionOptions collectionOptions) { return createMono(db -> db.createCollection(collectionName, collectionOptions)).map(success -> { @@ -1596,17 +1580,17 @@ public class ReactiveMongoTemplate implements ReactiveMongoOperations, Applicati * @param fields the document that specifies the fields to be returned. * @param entityClass the parameterized type of the returned list. * @param preparer allows for customization of the {@link DBCursor} used when iterating over the result set, (apply - * limits, skips and so on). + * limits, skips and so on). * @return the {@link List} of converted objects. */ protected Flux doFind(String collectionName, Document query, Document fields, Class entityClass, - FindPublisherPreparer preparer) { + FindPublisherPreparer preparer) { return doFind(collectionName, query, fields, entityClass, preparer, new ReadDocumentCallback(mongoConverter, entityClass, collectionName)); } protected Flux doFind(String collectionName, Document query, Document fields, Class entityClass, - FindPublisherPreparer preparer, DocumentCallback objectCallback) { + FindPublisherPreparer preparer, DocumentCallback objectCallback) { MongoPersistentEntity entity = mappingContext.getPersistentEntity(entityClass); @@ -1654,7 +1638,7 @@ public class ReactiveMongoTemplate implements ReactiveMongoOperations, Applicati * @return the List of converted objects. */ protected Mono doFindAndRemove(String collectionName, Document query, Document fields, Document sort, - Class entityClass) { + Class entityClass) { if (LOGGER.isDebugEnabled()) { LOGGER.debug(String.format("findAndRemove using query: %s fields: %s sort: %s for class: %s in collection: %s", @@ -1668,7 +1652,7 @@ public class ReactiveMongoTemplate implements ReactiveMongoOperations, Applicati } protected Mono doFindAndModify(String collectionName, Document query, Document fields, Document sort, - Class entityClass, Update update, FindAndModifyOptions options) { + Class entityClass, Update update, FindAndModifyOptions options) { FindAndModifyOptions optionsToUse; if (options == null) { @@ -1687,13 +1671,10 @@ public class ReactiveMongoTemplate implements ReactiveMongoOperations, Applicati Document mappedUpdate = updateMapper.getMappedObject(update.getUpdateObject(), entity); if (LOGGER.isDebugEnabled()) { - LOGGER - .debug( - String.format( - "findAndModify using query: %s fields: %s sort: %s for class: %s and update: %s " - + "in collection: %s", - serializeToJsonSafely(mappedQuery), fields, sort, entityClass, serializeToJsonSafely(mappedUpdate), - collectionName)); + LOGGER.debug(String.format( + "findAndModify using query: %s fields: %s sort: %s for class: %s and update: %s " + "in collection: %s", + serializeToJsonSafely(mappedQuery), fields, sort, entityClass, serializeToJsonSafely(mappedUpdate), + collectionName)); } return executeFindOneInternal(new FindAndModifyCallback(mappedQuery, fields, sort, mappedUpdate, optionsToUse), @@ -1828,7 +1809,7 @@ public class ReactiveMongoTemplate implements ReactiveMongoOperations, Applicati * @return */ private Mono executeFindOneInternal(ReactiveCollectionCallback collectionCallback, - DocumentCallback objectCallback, String collectionName) { + DocumentCallback objectCallback, String collectionName) { return createMono(collectionName, collection -> Mono.from(collectionCallback.doInCollection(collection)).map(objectCallback::doWith)); @@ -1846,14 +1827,14 @@ public class ReactiveMongoTemplate implements ReactiveMongoOperations, Applicati * * @param collectionCallback the callback to retrieve the {@link FindPublisher} with, must not be {@literal null}. * @param preparer the {@link FindPublisherPreparer} to potentially modify the {@link FindPublisher} before iterating - * over it, may be {@literal null} + * over it, may be {@literal null} * @param objectCallback the {@link DocumentCallback} to transform {@link Document}s into the actual domain type, must - * not be {@literal null}. + * not be {@literal null}. * @param collectionName the collection to be queried, must not be {@literal null}. * @return */ private Flux executeFindMultiInternal(ReactiveCollectionQueryCallback collectionCallback, - FindPublisherPreparer preparer, DocumentCallback objectCallback, String collectionName) { + FindPublisherPreparer preparer, DocumentCallback objectCallback, String collectionName) { return createFlux(collectionName, collection -> { @@ -1921,7 +1902,7 @@ public class ReactiveMongoTemplate implements ReactiveMongoOperations, Applicati * @return */ private static RuntimeException potentiallyConvertRuntimeException(RuntimeException ex, - PersistenceExceptionTranslator exceptionTranslator) { + PersistenceExceptionTranslator exceptionTranslator) { RuntimeException resolved = exceptionTranslator.translateExceptionIfPossible(ex); return resolved == null ? ex : resolved; } @@ -2137,7 +2118,7 @@ public class ReactiveMongoTemplate implements ReactiveMongoOperations, Applicati private final FindAndModifyOptions options; FindAndModifyCallback(Document query, Document fields, Document sort, Document update, - FindAndModifyOptions options) { + FindAndModifyOptions options) { this.query = query; this.fields = fields; @@ -2160,7 +2141,7 @@ public class ReactiveMongoTemplate implements ReactiveMongoOperations, Applicati } private FindOneAndUpdateOptions convertToFindOneAndUpdateOptions(FindAndModifyOptions options, Document fields, - Document sort) { + Document sort) { FindOneAndUpdateOptions result = new FindOneAndUpdateOptions(); @@ -2299,29 +2280,29 @@ public class ReactiveMongoTemplate implements ReactiveMongoOperations, Applicati this.type = type; } - public FindPublisher prepare(FindPublisher cursor) { + public FindPublisher prepare(FindPublisher findPublisher) { if (query == null) { - return cursor; + return findPublisher; } if (query.getSkip() <= 0 && query.getLimit() <= 0 && query.getSortObject() == null && !StringUtils.hasText(query.getHint()) && !query.getMeta().hasValues()) { - return cursor; + return findPublisher; } - FindPublisher cursorToUse = cursor; + FindPublisher findPublisherToUse = findPublisher; try { if (query.getSkip() > 0) { - cursorToUse = cursorToUse.skip(query.getSkip()); + findPublisherToUse = findPublisherToUse.skip(query.getSkip()); } if (query.getLimit() > 0) { - cursorToUse = cursorToUse.limit(query.getLimit()); + findPublisherToUse = findPublisherToUse.limit(query.getLimit()); } if (query.getSortObject() != null) { - Document sortDbo = type != null ? getMappedSortObject(query, type) : query.getSortObject(); - cursorToUse = cursorToUse.sort(sortDbo); + Document sort = type != null ? getMappedSortObject(query, type) : query.getSortObject(); + findPublisherToUse = findPublisherToUse.sort(sort); } BasicDBObject modifiers = new BasicDBObject(); @@ -2336,13 +2317,13 @@ public class ReactiveMongoTemplate implements ReactiveMongoOperations, Applicati } if (!modifiers.isEmpty()) { - cursorToUse = cursorToUse.modifiers(modifiers); + findPublisherToUse = findPublisherToUse.modifiers(modifiers); } } catch (RuntimeException e) { throw potentiallyConvertRuntimeException(e, exceptionTranslator); } - return cursorToUse; + return findPublisherToUse; } } @@ -2353,8 +2334,8 @@ public class ReactiveMongoTemplate implements ReactiveMongoOperations, Applicati } @Override - public FindPublisher prepare(FindPublisher cursor) { - return super.prepare(cursor.cursorType(CursorType.TailableAwait)); + public FindPublisher prepare(FindPublisher findPublisher) { + return super.prepare(findPublisher.cursorType(CursorType.TailableAwait)); } } @@ -2371,13 +2352,13 @@ public class ReactiveMongoTemplate implements ReactiveMongoOperations, Applicati @Override public Object resolveDbRef(MongoPersistentProperty property, DBRef dbref, DbRefResolverCallback callback, - DbRefProxyHandler proxyHandler) { + DbRefProxyHandler proxyHandler) { return null; } @Override public DBRef createDbRef(org.springframework.data.mongodb.core.mapping.DBRef annotation, - MongoPersistentEntity entity, Object id) { + MongoPersistentEntity entity, Object id) { return null; } @@ -2391,55 +2372,4 @@ public class ReactiveMongoTemplate implements ReactiveMongoOperations, Applicati return null; } } - - /** - * @author Mark Paluch - */ - private static class BlockingIndexOptionsProvider implements IndexOperationsProvider { - - private final ReactiveMongoOperations mongoOperations; - - public BlockingIndexOptionsProvider(ReactiveMongoOperations mongoOperations) { - this.mongoOperations = mongoOperations; - } - - @Override - public IndexOperations indexOps(String collectionName) { - return new BlockingIndexOperations(mongoOperations.reactiveIndexOps(collectionName)); - } - } - - /** - * Blocking {@link IndexOperations} overlay to synchronize calls. - * - * @author Mark Paluch - */ - private static class BlockingIndexOperations implements IndexOperations { - - private final ReactiveIndexOperations reactiveIndexOperations; - - public BlockingIndexOperations(ReactiveIndexOperations reactiveIndexOperations) { - this.reactiveIndexOperations = reactiveIndexOperations; - } - - @Override - public void ensureIndex(IndexDefinition indexDefinition) { - reactiveIndexOperations.ensureIndex(indexDefinition).block(); - } - - @Override - public void dropIndex(String name) { - reactiveIndexOperations.dropIndex(name).block(); - } - - @Override - public void dropAllIndexes() { - reactiveIndexOperations.dropAllIndexes().block(); - } - - @Override - public List getIndexInfo() { - return reactiveIndexOperations.getIndexInfo().collectList().block(); - } - } } diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/MongoConverters.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/MongoConverters.java index 261626838..4fa794936 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/MongoConverters.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/MongoConverters.java @@ -82,7 +82,6 @@ abstract class MongoConverters { converters.add(DocumentToNamedMongoScriptConverter.INSTANCE); converters.add(CurrencyToStringConverter.INSTANCE); converters.add(StringToCurrencyConverter.INSTANCE); - converters.add(NumberToNumberConverterFactory.INSTANCE); converters.add(AtomicIntegerToIntegerConverter.INSTANCE); converters.add(AtomicLongToLongConverter.INSTANCE); converters.add(LongToAtomicLongConverter.INSTANCE); @@ -297,26 +296,6 @@ abstract class MongoConverters { } } - @ReadingConverter - public static enum PublisherToFluxConverter implements Converter, Flux> { - - INSTANCE; - - /* - * (non-Javadoc) - * @see org.springframework.core.convert.converter.Converter#convert(java.lang.Object) - */ - - @Override - public Flux convert(Publisher source) { - - if(source instanceof Flux){ - return (Flux) source; - } - return Flux.from((Publisher) source); - } - } - /** * {@link Converter} implementation converting ISO 4217 {@link String} into {@link Currency}. * diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/index/IndexInfo.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/index/IndexInfo.java index fb8f673ce..5b8402970 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/index/IndexInfo.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/index/IndexInfo.java @@ -34,17 +34,15 @@ public class IndexInfo { private final String name; private final boolean unique; - private final boolean dropDuplicates; private final boolean sparse; private final String language; - public IndexInfo(List indexFields, String name, boolean unique, boolean dropDuplicates, boolean sparse, + public IndexInfo(List indexFields, String name, boolean unique, boolean sparse, String language) { this.indexFields = Collections.unmodifiableList(indexFields); this.name = name; this.unique = unique; - this.dropDuplicates = dropDuplicates; this.sparse = sparse; this.language = language; } @@ -84,10 +82,6 @@ public class IndexInfo { return unique; } - public boolean isDropDuplicates() { - return dropDuplicates; - } - public boolean isSparse() { return sparse; } @@ -102,8 +96,7 @@ public class IndexInfo { @Override public String toString() { - return "IndexInfo [indexFields=" + indexFields + ", name=" + name + ", unique=" + unique + ", dropDuplicates=" - + dropDuplicates + ", sparse=" + sparse + ", language=" + language + "]"; + return "IndexInfo [indexFields=" + indexFields + ", name=" + name + ", unique=" + unique + ", sparse=" + sparse + ", language=" + language + "]"; } @Override @@ -111,7 +104,6 @@ public class IndexInfo { final int prime = 31; int result = 1; - result = prime * result + (dropDuplicates ? 1231 : 1237); result = prime * result + ObjectUtils.nullSafeHashCode(indexFields); result = prime * result + ((name == null) ? 0 : name.hashCode()); result = prime * result + (sparse ? 1231 : 1237); @@ -132,9 +124,6 @@ public class IndexInfo { return false; } IndexInfo other = (IndexInfo) obj; - if (dropDuplicates != other.dropDuplicates) { - return false; - } if (indexFields == null) { if (other.indexFields != null) { return false; diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/InfiniteStream.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/InfiniteStream.java index 2e2897cc2..1448450c7 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/InfiniteStream.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/InfiniteStream.java @@ -37,7 +37,7 @@ import reactor.core.Cancellation; *

* A stream that is no longer in use must be {@link Cancellation#dispose()} disposed} otherwise the streams will linger * and exhaust resources. - * + * * @author Mark Paluch * @see Tailable Cursors */ diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/config/EnableReactiveMongoRepositories.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/config/EnableReactiveMongoRepositories.java index 8be556629..f6ddd83d9 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/config/EnableReactiveMongoRepositories.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/config/EnableReactiveMongoRepositories.java @@ -37,7 +37,7 @@ import org.springframework.data.repository.query.QueryLookupStrategy.Key; * Annotation to activate reactive 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 Mark Paluch * @since 2.0 */ 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 index aed4785ec..6aa5fd713 100644 --- 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 @@ -61,7 +61,7 @@ public class MongoRepositoryConfigurationExtension extends RepositoryConfigurati return "MongoDB"; } - /* + /* * (non-Javadoc) * @see org.springframework.data.repository.config.RepositoryConfigurationExtensionSupport#getModulePrefix() */ @@ -152,7 +152,7 @@ public class MongoRepositoryConfigurationExtension extends RepositoryConfigurati return repositoryConfigurations.stream().filter(configuration -> { - Class repositoryInterface = super.loadRepositoryInterface(configuration, loader); + Class repositoryInterface = loadRepositoryInterface(configuration, loader); return !RepositoryType.isReactiveRepository(repositoryInterface); }).collect(Collectors.toList()); diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/config/ReactiveMongoRepositoriesRegistrar.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/config/ReactiveMongoRepositoriesRegistrar.java index e7675b0ec..b0ea54f2f 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/config/ReactiveMongoRepositoriesRegistrar.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/config/ReactiveMongoRepositoriesRegistrar.java @@ -24,12 +24,13 @@ import org.springframework.data.repository.config.RepositoryConfigurationExtensi /** * Mongo-specific {@link ImportBeanDefinitionRegistrar}. - * + * * @author Mark Paluch + * @since 2.0 */ class ReactiveMongoRepositoriesRegistrar extends RepositoryBeanDefinitionRegistrarSupport { - /* + /* * (non-Javadoc) * @see org.springframework.data.repository.config.RepositoryBeanDefinitionRegistrarSupport#getAnnotation() */ @@ -38,7 +39,7 @@ class ReactiveMongoRepositoriesRegistrar extends RepositoryBeanDefinitionRegistr return EnableReactiveMongoRepositories.class; } - /* + /* * (non-Javadoc) * @see org.springframework.data.repository.config.RepositoryBeanDefinitionRegistrarSupport#getExtension() */ diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/config/ReactiveMongoRepositoryConfigurationExtension.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/config/ReactiveMongoRepositoryConfigurationExtension.java index 32fbbf54c..04678d3d8 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/config/ReactiveMongoRepositoryConfigurationExtension.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/config/ReactiveMongoRepositoryConfigurationExtension.java @@ -45,6 +45,8 @@ import org.w3c.dom.Element; * Reactive {@link RepositoryConfigurationExtension} for MongoDB. * * @author Mark Paluch + * @author Christoph Strobl + * @since 2.0 */ public class ReactiveMongoRepositoryConfigurationExtension extends RepositoryConfigurationExtensionSupport { @@ -161,11 +163,8 @@ public class ReactiveMongoRepositoryConfigurationExtension extends RepositoryCon Collection> repositoryConfigurations = super.getRepositoryConfigurations(configSource, loader, strictMatchesOnly); - return repositoryConfigurations.stream().filter(configuration -> { - - Class repositoryInterface = super.loadRepositoryInterface(configuration, loader); - return RepositoryType.isReactiveRepository(repositoryInterface); - - }).collect(Collectors.toList()); + return repositoryConfigurations.stream() + .filter(configuration -> RepositoryType.isReactiveRepository(loadRepositoryInterface(configuration, loader))) + .collect(Collectors.toList()); } } diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/config/RepositoryType.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/config/RepositoryType.java index aa7457f0e..55f51df5d 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/config/RepositoryType.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/config/RepositoryType.java @@ -16,8 +16,11 @@ package org.springframework.data.mongodb.repository.config; import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.List; import org.springframework.data.repository.util.ReactiveWrappers; +import org.springframework.util.ReflectionUtils; import lombok.experimental.UtilityClass; @@ -25,6 +28,8 @@ import lombok.experimental.UtilityClass; * Utility class to discover whether a repository interface uses reactive wrapper types. * * @author Mark Paluch + * @author Christoph Strobl + * @since 2.0 */ @UtilityClass class RepositoryType { @@ -41,16 +46,9 @@ class RepositoryType { return false; } - Method[] methods = repositoryInterface.getMethods(); - - for (Method method : methods) { - - if (usesReactiveWrappers(method)) { - return true; - } - } - - return false; + List reactiveMethods = new ArrayList<>(); + ReflectionUtils.doWithMethods(repositoryInterface, reactiveMethods::add, RepositoryType::usesReactiveWrappers); + return !reactiveMethods.isEmpty(); } private static boolean usesReactiveWrappers(Method method) { diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/AbstractReactiveMongoQuery.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/AbstractReactiveMongoQuery.java index dda007048..4c3c987f4 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/AbstractReactiveMongoQuery.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/AbstractReactiveMongoQuery.java @@ -44,22 +44,24 @@ import reactor.core.publisher.Mono; * Base class for reactive {@link RepositoryQuery} implementations for MongoDB. * * @author Mark Paluch + * @author Christoph Strobl + * @since 2.0 */ public abstract class AbstractReactiveMongoQuery implements RepositoryQuery { - private final MongoQueryMethod method; + private final ReactiveMongoQueryMethod method; private final ReactiveMongoOperations operations; private final EntityInstantiators instantiators; /** * Creates a new {@link AbstractReactiveMongoQuery} from the given {@link MongoQueryMethod} and * {@link MongoOperations}. - * + * * @param method must not be {@literal null}. * @param operations must not be {@literal null}. * @param conversionService must not be {@literal null}. */ - public AbstractReactiveMongoQuery(MongoQueryMethod method, ReactiveMongoOperations operations, + public AbstractReactiveMongoQuery(ReactiveMongoQueryMethod method, ReactiveMongoOperations operations, ConversionService conversionService) { Assert.notNull(method, "MongoQueryMethod must not be null!"); @@ -84,13 +86,8 @@ public abstract class AbstractReactiveMongoQuery implements RepositoryQuery { */ public Object execute(Object[] parameters) { - boolean hasReactiveParameters = hasReactiveWrapperParameter(); - - if (hasReactiveParameters) { - return executeDeferred(parameters); - } - - return execute(new MongoParametersParameterAccessor(method, parameters)); + return method.hasReactiveWrapperParameter() ? executeDeferred(parameters) + : execute(new MongoParametersParameterAccessor(method, parameters)); } @SuppressWarnings("unchecked") @@ -120,16 +117,6 @@ public abstract class AbstractReactiveMongoQuery implements RepositoryQuery { return execution.execute(query, processor.getReturnedType().getDomainType(), collection); } - private boolean hasReactiveWrapperParameter() { - - for (MongoParameters.MongoParameter mongoParameter : method.getParameters()) { - if (ReactiveWrapperConverters.supports(mongoParameter.getType())) { - return true; - } - } - return false; - } - /** * Returns the execution instance to use. * @@ -153,7 +140,7 @@ public abstract class AbstractReactiveMongoQuery implements RepositoryQuery { return new SlicedExecution(operations, accessor.getPageable()); } else if (isInfiniteStream(method)) { return new TailExecution(operations, accessor.getPageable()); - } else if (method.isCollectionQuery()) { + } else if (method.isCollectionQuery()) { return new CollectionExecution(operations, accessor.getPageable()); } else if (method.isPageQuery()) { return new PagedExecution(operations, accessor.getPageable()); diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/ReactiveMongoParameterAccessor.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/ReactiveMongoParameterAccessor.java index b7dd7304e..99907d038 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/ReactiveMongoParameterAccessor.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/ReactiveMongoParameterAccessor.java @@ -15,6 +15,9 @@ */ package org.springframework.data.mongodb.repository.query; +import java.util.ArrayList; +import java.util.List; + import org.springframework.data.repository.util.ReactiveWrapperConverters; import org.springframework.data.repository.util.ReactiveWrappers; @@ -27,35 +30,34 @@ import reactor.core.publisher.MonoProcessor; * to reactive parameter wrapper types upon creation. This class performs synchronization when acessing parameters. * * @author Mark Paluch + * @author Christoph Strobl + * @since 2.0 */ class ReactiveMongoParameterAccessor extends MongoParametersParameterAccessor { private final Object[] values; - private final MonoProcessor[] subscriptions; + private final List> subscriptions; public ReactiveMongoParameterAccessor(MongoQueryMethod method, Object[] values) { super(method, values); this.values = values; - this.subscriptions = new MonoProcessor[values.length]; + this.subscriptions = new ArrayList<>(values.length); for (int i = 0; i < values.length; i++) { Object value = values[i]; - if (value == null) { - continue; - } - - if (!ReactiveWrappers.supports(value.getClass())) { + if (value == null || !ReactiveWrappers.supports(value.getClass())) { + subscriptions.add(null); continue; } if (ReactiveWrappers.isSingleValueType(value.getClass())) { - subscriptions[i] = ReactiveWrapperConverters.toWrapper(value, Mono.class).subscribe(); + subscriptions.add(ReactiveWrapperConverters.toWrapper(value, Mono.class).subscribe()); } else { - subscriptions[i] = ReactiveWrapperConverters.toWrapper(value, Flux.class).collectList().subscribe(); + subscriptions.add(ReactiveWrapperConverters.toWrapper(value, Flux.class).collectList().subscribe()); } } } @@ -67,8 +69,8 @@ class ReactiveMongoParameterAccessor extends MongoParametersParameterAccessor { @Override protected T getValue(int index) { - if (subscriptions[index] != null) { - return (T) subscriptions[index].block(); + if (subscriptions.get(index) != null) { + return (T) subscriptions.get(index).block(); } return super.getValue(index); diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/ReactiveMongoQueryMethod.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/ReactiveMongoQueryMethod.java index 60bf57255..ef0713f3f 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/ReactiveMongoQueryMethod.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/ReactiveMongoQueryMethod.java @@ -27,8 +27,10 @@ import org.springframework.data.geo.GeoResult; import org.springframework.data.mapping.context.MappingContext; import org.springframework.data.mongodb.core.mapping.MongoPersistentEntity; import org.springframework.data.mongodb.core.mapping.MongoPersistentProperty; +import org.springframework.data.mongodb.repository.query.MongoParameters.MongoParameter; import org.springframework.data.projection.ProjectionFactory; import org.springframework.data.repository.core.RepositoryMetadata; +import org.springframework.data.repository.util.ReactiveWrapperConverters; import org.springframework.data.repository.util.ReactiveWrappers; import org.springframework.data.util.ClassTypeInformation; import org.springframework.data.util.TypeInformation; @@ -37,6 +39,7 @@ import org.springframework.data.util.TypeInformation; * Reactive specific implementation of {@link MongoQueryMethod}. * * @author Mark Paluch + * @author Christoph Strobl * @since 2.0 */ public class ReactiveMongoQueryMethod extends MongoQueryMethod { @@ -147,4 +150,21 @@ public class ReactiveMongoQueryMethod extends MongoQueryMethod { public boolean isStreamQuery() { return true; } + + /** + * Check if the given {@link org.springframework.data.repository.query.QueryMethod} receives a reactive parameter + * wrapper as one of its parameters. + * + * @return + */ + public boolean hasReactiveWrapperParameter() { + + for (MongoParameter mongoParameter : getParameters()) { + if (ReactiveWrapperConverters.supports(mongoParameter.getType())) { + return true; + } + } + return false; + } + } diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/ReactivePartTreeMongoQuery.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/ReactivePartTreeMongoQuery.java index e974991fe..61d861008 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/ReactivePartTreeMongoQuery.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/ReactivePartTreeMongoQuery.java @@ -15,6 +15,7 @@ */ package org.springframework.data.mongodb.repository.query; +import org.bson.Document; import org.springframework.core.convert.ConversionService; import org.springframework.data.mapping.context.MappingContext; import org.springframework.data.mongodb.core.MongoTemplate; @@ -37,6 +38,8 @@ import com.mongodb.util.JSONParseException; * Reactive PartTree {@link RepositoryQuery} implementation for Mongo. * * @author Mark Paluch + * @author Christoph Strobl + * @since 2.0 */ public class ReactivePartTreeMongoQuery extends AbstractReactiveMongoQuery { @@ -52,7 +55,7 @@ public class ReactivePartTreeMongoQuery extends AbstractReactiveMongoQuery { * @param mongoOperations must not be {@literal null}. * @param conversionService must not be {@literal null}. */ - public ReactivePartTreeMongoQuery(MongoQueryMethod method, ReactiveMongoOperations mongoOperations, ConversionService conversionService) { + public ReactivePartTreeMongoQuery(ReactiveMongoQueryMethod method, ReactiveMongoOperations mongoOperations, ConversionService conversionService) { super(method, mongoOperations, conversionService); @@ -90,19 +93,14 @@ public class ReactivePartTreeMongoQuery extends AbstractReactiveMongoQuery { query.addCriteria(textCriteria); } - String fieldSpec = this.getQueryMethod().getFieldSpecification(); + String fieldSpec = getQueryMethod().getFieldSpecification(); if (!StringUtils.hasText(fieldSpec)) { ReturnedType returnedType = processor.withDynamicProjection(accessor).getReturnedType(); if (returnedType.isProjecting()) { - - Field fields = query.fields(); - - for (String field : returnedType.getInputProperties()) { - fields.include(field); - } + returnedType.getInputProperties().forEach(query.fields()::include); } return query; @@ -110,7 +108,7 @@ public class ReactivePartTreeMongoQuery extends AbstractReactiveMongoQuery { try { - BasicQuery result = new BasicQuery(query.getQueryObject().toJson(), fieldSpec); + BasicQuery result = new BasicQuery(query.getQueryObject(), Document.parse(fieldSpec)); result.setSortObject(query.getSortObject()); return result; diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/ReactiveStringBasedMongoQuery.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/ReactiveStringBasedMongoQuery.java index a2426008d..c40f37502 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/ReactiveStringBasedMongoQuery.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/ReactiveStringBasedMongoQuery.java @@ -36,6 +36,8 @@ import org.springframework.util.Assert; * Query to use a plain JSON String to create the {@link Query} to actually execute. * * @author Mark Paluch + * @author Christoph Strobl + * @since 2.0 */ public class ReactiveStringBasedMongoQuery extends AbstractReactiveMongoQuery { @@ -60,7 +62,7 @@ public class ReactiveStringBasedMongoQuery extends AbstractReactiveMongoQuery { * @param evaluationContextProvider must not be {@literal null}. * @param conversionService must not be {@literal null}. */ - public ReactiveStringBasedMongoQuery(MongoQueryMethod method, ReactiveMongoOperations mongoOperations, + public ReactiveStringBasedMongoQuery(ReactiveMongoQueryMethod method, ReactiveMongoOperations mongoOperations, SpelExpressionParser expressionParser, EvaluationContextProvider evaluationContextProvider, ConversionService conversionService) { this(method.getAnnotatedQuery(), method, mongoOperations, expressionParser, evaluationContextProvider, conversionService); } @@ -75,7 +77,7 @@ public class ReactiveStringBasedMongoQuery extends AbstractReactiveMongoQuery { * @param expressionParser must not be {@literal null}. * @param conversionService must not be {@literal null}. */ - public ReactiveStringBasedMongoQuery(String query, MongoQueryMethod method, ReactiveMongoOperations mongoOperations, + public ReactiveStringBasedMongoQuery(String query, ReactiveMongoQueryMethod method, ReactiveMongoOperations mongoOperations, SpelExpressionParser expressionParser, EvaluationContextProvider evaluationContextProvider, ConversionService conversionService) { super(method, mongoOperations, conversionService); diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/support/IndexEnsuringQueryCreationListener.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/support/IndexEnsuringQueryCreationListener.java index 7ba26332c..4faa11e54 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/support/IndexEnsuringQueryCreationListener.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/support/IndexEnsuringQueryCreationListener.java @@ -22,6 +22,7 @@ import java.util.Set; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.data.domain.Sort; +import org.springframework.data.domain.Sort.Order; import org.springframework.data.domain.Sort.Direction; import org.springframework.data.mongodb.core.IndexOperationsProvider; import org.springframework.data.mongodb.core.MongoOperations; @@ -40,6 +41,7 @@ import org.springframework.util.Assert; * * @author Oliver Gierke * @author Mark Paluch + * @author Christoph Strobl */ class IndexEnsuringQueryCreationListener implements QueryCreationListener { @@ -81,7 +83,7 @@ class IndexEnsuringQueryCreationListener implements QueryCreationListener getRepositoryBaseClass(RepositoryMetadata metadata) { + + boolean isReactiveRepository = (PROJECT_REACTOR_PRESENT && ReactiveCrudRepository.class.isAssignableFrom(metadata.getRepositoryInterface())) || ( RXJAVA_OBSERVABLE_PRESENT && RxJavaCrudRepository.class.isAssignableFrom(metadata.getRepositoryInterface())); + boolean isQueryDslRepository = QUERY_DSL_PRESENT + && QueryDslPredicateExecutor.class.isAssignableFrom(metadata.getRepositoryInterface()); + if (isReactiveRepository) { + + if(isQueryDslRepository) { + throw new InvalidDataAccessApiUsageException("Cannot combine Querydsl and reactive repository in one interface"); + } return SimpleReactiveMongoRepository.class; } - - boolean isQueryDslRepository = QUERY_DSL_PRESENT - && QueryDslPredicateExecutor.class.isAssignableFrom(metadata.getRepositoryInterface()); - return isQueryDslRepository ? QueryDslMongoRepository.class : SimpleMongoRepository.class; } diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/support/ReactiveChunk.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/support/ReactiveChunk.java index 029d7ca8a..43250244d 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/support/ReactiveChunk.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/support/ReactiveChunk.java @@ -35,7 +35,10 @@ import reactor.core.publisher.MonoProcessor; * A reactive chunk of data restricted by the configured {@link Pageable}. * * @author Mark Paluch + * @author Christoph Strobl + * @since 2.0 */ +//TODO: should that one move to SD Commons abstract class ReactiveChunk implements Slice, Serializable { private static final long serialVersionUID = 867755909294344406L; @@ -88,7 +91,7 @@ abstract class ReactiveChunk implements Slice, Serializable { * @see org.springframework.data.domain.Slice#getNumberOfElements() */ public int getNumberOfElements() { - return getContent0().size(); + return getContent().size(); } /* @@ -141,7 +144,7 @@ abstract class ReactiveChunk implements Slice, Serializable { * @see org.springframework.data.domain.Slice#hasContent() */ public boolean hasContent() { - return !getContent0().isEmpty(); + return !getContent().isEmpty(); } /* @@ -149,7 +152,18 @@ abstract class ReactiveChunk implements Slice, Serializable { * @see org.springframework.data.domain.Slice#getContent() */ public List getContent() { - return Collections.unmodifiableList(getContent0()); + + if (contentCache != null) { + return Collections.unmodifiableList(contentCache); + } + + List list = processor.block(); + + if (list.size() > pageable.getPageSize()) { + return list.subList(0, pageable.getPageSize()); + } + + return Collections.unmodifiableList(list); } /* @@ -165,7 +179,7 @@ abstract class ReactiveChunk implements Slice, Serializable { * @see java.lang.Iterable#iterator() */ public Iterator iterator() { - return getContent0().iterator(); + return getContent().iterator(); } /** @@ -178,7 +192,7 @@ abstract class ReactiveChunk implements Slice, Serializable { Assert.notNull(converter, "Converter must not be null!"); - List result = new ArrayList(getContent0().size()); + List result = new ArrayList(getContent().size()); for (T element : this) { result.add(converter.convert(element)); @@ -187,21 +201,6 @@ abstract class ReactiveChunk implements Slice, Serializable { return result; } - protected List getContent0() { - - if (contentCache != null) { - return contentCache; - } - - List list = processor.block(); - - if (list.size() > pageable.getPageSize()) { - return list.subList(0, pageable.getPageSize()); - } - - return list; - } - /** * Returns whether the returned list contains more elements than specified by {@link Pageable#getPageSize()}. * diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/support/ReactiveMongoRepositoryFactory.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/support/ReactiveMongoRepositoryFactory.java index ad763d71e..4c3e5617e 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/support/ReactiveMongoRepositoryFactory.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/support/ReactiveMongoRepositoryFactory.java @@ -54,6 +54,7 @@ import org.springframework.util.ClassUtils; * Factory to create {@link org.springframework.data.mongodb.repository.ReactiveMongoRepository} instances. * * @author Mark Paluch + * @author Christoph Strobl * @since 2.0 */ public class ReactiveMongoRepositoryFactory extends RepositoryFactorySupport { @@ -140,7 +141,7 @@ public class ReactiveMongoRepositoryFactory extends RepositoryFactorySupport { /** * Reactive MongoDB support requires reactive wrapper support. If return type/parameters are reactive wrapper types, * then it's required to be able to convert these into Publisher. - * + * * @param method the method to validate. */ private static void validate(Method method) { @@ -212,7 +213,7 @@ public class ReactiveMongoRepositoryFactory extends RepositoryFactorySupport { public RepositoryQuery resolveQuery(Method method, RepositoryMetadata metadata, ProjectionFactory factory, NamedQueries namedQueries) { - MongoQueryMethod queryMethod = new ReactiveMongoQueryMethod(method, metadata, factory, mappingContext); + ReactiveMongoQueryMethod queryMethod = new ReactiveMongoQueryMethod(method, metadata, factory, mappingContext); String namedQueryName = queryMethod.getNamedQueryName(); if (namedQueries.hasQuery(namedQueryName)) { diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/support/ReactiveMongoRepositoryFactoryBean.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/support/ReactiveMongoRepositoryFactoryBean.java index bce55f3fe..c51397bb7 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/support/ReactiveMongoRepositoryFactoryBean.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/support/ReactiveMongoRepositoryFactoryBean.java @@ -19,6 +19,7 @@ package org.springframework.data.mongodb.repository.support; import java.io.Serializable; import org.springframework.data.mapping.context.MappingContext; +import org.springframework.data.mongodb.core.IndexOperationsAdapter; import org.springframework.data.mongodb.core.ReactiveMongoOperations; import org.springframework.data.repository.Repository; import org.springframework.data.repository.core.support.RepositoryFactoryBeanSupport; @@ -30,6 +31,7 @@ import org.springframework.util.Assert; * {@link org.springframework.data.mongodb.repository.ReactiveMongoRepository} instances. * * @author Mark Paluch + * @author Christoph Strobl * @since 2.0 * @see org.springframework.data.repository.reactive.ReactivePagingAndSortingRepository * @see org.springframework.data.repository.reactive.RxJavaPagingAndSortingRepository @@ -84,7 +86,7 @@ public class ReactiveMongoRepositoryFactoryBean, S, if (createIndexesForQueryMethods) { factory.addQueryCreationListener( - new IndexEnsuringQueryCreationListener(collectionName -> operations.indexOps(collectionName))); + new IndexEnsuringQueryCreationListener(collectionName -> IndexOperationsAdapter.blocking(operations.reactiveIndexOps(collectionName)))); } return factory; diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/support/ReactivePageImpl.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/support/ReactivePageImpl.java index 5ca3f5956..bc981e3f8 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/support/ReactivePageImpl.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/support/ReactivePageImpl.java @@ -32,6 +32,7 @@ import reactor.core.publisher.MonoProcessor; * @author Mark Paluch * @since 2.0 */ +//TODO: should that one move to SD-Commons? public class ReactivePageImpl extends ReactiveChunk implements Page { private static final long serialVersionUID = 867755909294344406L; diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/support/SimpleReactiveMongoRepository.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/support/SimpleReactiveMongoRepository.java index 5cbb5c12f..dfdee4564 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/support/SimpleReactiveMongoRepository.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/support/SimpleReactiveMongoRepository.java @@ -216,9 +216,7 @@ public class SimpleReactiveMongoRepository implement Assert.notNull(entities, "The given Publisher of entities must not be null!"); - return Flux.from(entities).flatMap(entity -> { - return mongoOperations.insert(entity, entityInformation.getCollectionName()); - }); + return Flux.from(entities).flatMap(entity -> mongoOperations.insert(entity, entityInformation.getCollectionName())); } public Mono save(S entity) { @@ -272,15 +270,17 @@ public class SimpleReactiveMongoRepository implement }); } + // TODO: should this one really be void? public Mono delete(ID id) { Assert.notNull(id, "The given id must not be null!"); return mongoOperations - .remove(getIdQuery(id), entityInformation.getJavaType(), entityInformation.getCollectionName()) - .then(); + .remove(getIdQuery(id), entityInformation.getJavaType(), entityInformation.getCollectionName()).then(); + } + // TODO: should this one really be void? public Mono delete(T entity) { Assert.notNull(entity, "The given entity must not be null!"); @@ -288,6 +288,7 @@ public class SimpleReactiveMongoRepository implement return delete(entityInformation.getId(entity)); } + // TODO: should this one really be void? public Mono delete(Iterable entities) { Assert.notNull(entities, "The given Iterable of entities must not be null!"); @@ -295,6 +296,7 @@ public class SimpleReactiveMongoRepository implement return Flux.fromIterable(entities).flatMap(entity -> delete(entityInformation.getId(entity))).then(); } + // TODO: should this one really be void? @Override public Mono delete(Publisher entityStream) { @@ -303,6 +305,7 @@ public class SimpleReactiveMongoRepository implement return Flux.from(entityStream).flatMap(entity -> delete(entityInformation.getId(entity))).then(); } + // TODO: should this one really be void? public Mono deleteAll() { return mongoOperations.remove(new Query(), entityInformation.getCollectionName()) .then(deleteResult -> Mono.empty()); 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 index d4a6ce7c5..d88279632 100644 --- 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 @@ -48,7 +48,7 @@ import example.second.Second; /** * Unit tests for {@link AbstractMongoConfiguration}. - * + * * @author Oliver Gierke * @author Thomas Darimont * @author Mark Paluch diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/config/AbstractReactiveMongoConfigurationIntegrationTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/config/AbstractReactiveMongoConfigurationIntegrationTests.java index 3dcb3c25d..b995e8a38 100644 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/config/AbstractReactiveMongoConfigurationIntegrationTests.java +++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/config/AbstractReactiveMongoConfigurationIntegrationTests.java @@ -33,7 +33,7 @@ import com.mongodb.reactivestreams.client.MongoClients; /** * Integration tests for {@link AbstractReactiveMongoConfiguration}. - * + * * @author Mark Paluch */ @RunWith(SpringRunner.class) diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/config/AbstractReactiveMongoConfigurationUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/config/AbstractReactiveMongoConfigurationUnitTests.java index f73f4da6e..09d9e52d3 100644 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/config/AbstractReactiveMongoConfigurationUnitTests.java +++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/config/AbstractReactiveMongoConfigurationUnitTests.java @@ -50,7 +50,7 @@ import example.second.Second; /** * Unit tests for {@link AbstractReactiveMongoConfiguration}. - * + * * @author Mark Paluch */ public class AbstractReactiveMongoConfigurationUnitTests { @@ -74,7 +74,6 @@ public class AbstractReactiveMongoConfigurationUnitTests { */ @Test public void doesNotScanPackageIfMappingPackageIsNull() throws ClassNotFoundException { - assertScanningDisabled(null); } 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 9bcd66747..aca1cab1a 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 @@ -376,7 +376,6 @@ public class MongoTemplateTests { assertThat(indexInfoList.size(), is(2)); IndexInfo ii = indexInfoList.get(1); assertThat(ii.isUnique(), is(true)); - assertThat(ii.isDropDuplicates(), is(false)); assertThat(ii.isSparse(), is(false)); List indexFields = ii.getIndexFields(); diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/ReactiveMongoTemplateExecuteTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/ReactiveMongoTemplateExecuteTests.java index 8fb8e29d2..93d42a1f0 100644 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/ReactiveMongoTemplateExecuteTests.java +++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/ReactiveMongoTemplateExecuteTests.java @@ -33,6 +33,7 @@ import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.dao.InvalidDataAccessApiUsageException; import org.springframework.data.mongodb.UncategorizedMongoDbException; +import org.springframework.data.util.Version; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; @@ -52,22 +53,22 @@ import reactor.test.TestSubscriber; @ContextConfiguration("classpath:reactive-infrastructure.xml") public class ReactiveMongoTemplateExecuteTests { - private static final org.springframework.data.util.Version THREE = org.springframework.data.util.Version.parse("3.0"); + private static final Version THREE = Version.parse("3.0"); @Autowired SimpleReactiveMongoDatabaseFactory factory; @Autowired ReactiveMongoOperations operations; @Rule public ExpectedException thrown = ExpectedException.none(); - org.springframework.data.util.Version mongoVersion; + Version mongoVersion; @Before public void setUp() { cleanUp(); if (mongoVersion == null) { - org.bson.Document result = operations.executeCommand("{ buildInfo: 1 }").block(); - mongoVersion = org.springframework.data.util.Version.parse(result.get("version").toString()); + Document result = operations.executeCommand("{ buildInfo: 1 }").block(); + mongoVersion = Version.parse(result.get("version").toString()); } } diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/ReactiveMongoTemplateIndexTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/ReactiveMongoTemplateIndexTests.java index 9fb52c348..b69013e54 100644 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/ReactiveMongoTemplateIndexTests.java +++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/ReactiveMongoTemplateIndexTests.java @@ -31,7 +31,6 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.annotation.Id; import org.springframework.data.domain.Sort.Direction; import org.springframework.data.mongodb.core.index.Index; -import org.springframework.data.mongodb.core.index.Index.Duplicates; import org.springframework.data.mongodb.core.index.IndexField; import org.springframework.data.mongodb.core.index.IndexInfo; import org.springframework.data.util.Version; @@ -50,14 +49,12 @@ import reactor.test.TestSubscriber; * Integration test for {@link MongoTemplate}. * * @author Mark Paluch + * @author Christoph Strobl */ @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration("classpath:reactive-infrastructure.xml") public class ReactiveMongoTemplateIndexTests { - private static final org.springframework.data.util.Version TWO_DOT_EIGHT = org.springframework.data.util.Version - .parse("2.8"); - @Autowired SimpleReactiveMongoDatabaseFactory factory; @Autowired ReactiveMongoTemplate template; @@ -68,20 +65,11 @@ public class ReactiveMongoTemplateIndexTests { @Before public void setUp() { cleanDb(); - queryMongoVersionIfNecessary(); } @After public void cleanUp() {} - private void queryMongoVersionIfNecessary() { - - if (mongoVersion == null) { - org.bson.Document result = template.executeCommand("{ buildInfo: 1 }").block(); - mongoVersion = Version.parse(result.get("version").toString()); - } - } - private void cleanDb() { template.dropCollection(Person.class).block(); } @@ -90,7 +78,6 @@ public class ReactiveMongoTemplateIndexTests { * @see DATAMONGO-1444 */ @Test - @SuppressWarnings("deprecation") public void testEnsureIndexShouldCreateIndex() { Person p1 = new Person("Oliver"); @@ -100,8 +87,7 @@ public class ReactiveMongoTemplateIndexTests { p2.setAge(40); template.insert(p2); - template.reactiveIndexOps(Person.class).ensureIndex(new Index().on("age", Direction.DESC).unique()) - .block(); + template.reactiveIndexOps(Person.class).ensureIndex(new Index().on("age", Direction.DESC).unique()).block(); MongoCollection coll = template.getCollection(template.getCollectionName(Person.class)); List indexInfo = Flux.from(coll.listIndexes()).collectList().block(); @@ -109,14 +95,14 @@ public class ReactiveMongoTemplateIndexTests { assertThat(indexInfo.size(), is(2)); Object indexKey = null; boolean unique = false; - for (org.bson.Document ix : indexInfo) { + for (Document ix : indexInfo) { if ("age_-1".equals(ix.get("name"))) { indexKey = ix.get("key"); unique = (Boolean) ix.get("unique"); } } - assertThat(((org.bson.Document) indexKey), hasEntry("age", -1)); + assertThat(((Document) indexKey), hasEntry("age", -1)); assertThat(unique, is(true)); } @@ -124,15 +110,13 @@ public class ReactiveMongoTemplateIndexTests { * @see DATAMONGO-1444 */ @Test - @SuppressWarnings("deprecation") public void getIndexInfoShouldReturnCorrectIndex() { Person p1 = new Person("Oliver"); p1.setAge(25); template.insert(p1).block(); - template.reactiveIndexOps(Person.class).ensureIndex(new Index().on("age", Direction.DESC).unique()) - .block(); + template.reactiveIndexOps(Person.class).ensureIndex(new Index().on("age", Direction.DESC).unique()).block(); List indexInfoList = Flux.from(template.reactiveIndexOps(Person.class).getIndexInfo()).collectList() .block(); @@ -140,7 +124,6 @@ public class ReactiveMongoTemplateIndexTests { IndexInfo ii = indexInfoList.get(1); assertThat(ii.isUnique(), is(true)); - assertThat(ii.isDropDuplicates(), is(false)); assertThat(ii.isSparse(), is(false)); List indexFields = ii.getIndexFields(); @@ -168,7 +151,7 @@ public class ReactiveMongoTemplateIndexTests { ListIndexesPublisher listIndexesPublisher = template .getCollection(template.getCollectionName(Person.class)).listIndexes(); List indexInfo = Flux.from(listIndexesPublisher).collectList().block(); - org.bson.Document indexKey = null; + Document indexKey = null; boolean unique = false; for (Document document : indexInfo) { diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/ReactiveMongoTemplateTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/ReactiveMongoTemplateTests.java index 48e926cee..197a3467e 100644 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/ReactiveMongoTemplateTests.java +++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/ReactiveMongoTemplateTests.java @@ -74,6 +74,7 @@ import reactor.test.TestSubscriber; * Integration test for {@link MongoTemplate}. * * @author Mark Paluch + * @author Christoph Strobl */ @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration("classpath:reactive-infrastructure.xml") @@ -89,20 +90,11 @@ public class ReactiveMongoTemplateTests { @Before public void setUp() { cleanDb(); - queryMongoVersionIfNecessary(); } @After public void cleanUp() {} - private void queryMongoVersionIfNecessary() { - - if (mongoVersion == null) { - org.bson.Document result = template.executeCommand("{ buildInfo: 1 }").block(); - mongoVersion = org.springframework.data.util.Version.parse(result.get("version").toString()); - } - } - private void cleanDb() { template.dropCollection("people") // .and(template.dropCollection("collection")) // @@ -181,7 +173,7 @@ public class ReactiveMongoTemplateTests { template.insert(person).block(); TestSubscriber testSubscriber = TestSubscriber.create(); - Flux flux = template.find(new Query(Criteria.where("_id").is(person.getId())), Person.class); + Flux flux = template.find(new Query(where("_id").is(person.getId())), Person.class); flux.subscribe(testSubscriber); testSubscriber.awaitAndAssertNextValueCount(1); @@ -225,7 +217,7 @@ public class ReactiveMongoTemplateTests { template.insert(person, "people").block(); TestSubscriber testSubscriber = TestSubscriber.create(); - Flux flux = template.find(new Query(Criteria.where("_id").is(person.getId())), Person.class, "people"); + Flux flux = template.find(new Query(where("_id").is(person.getId())), Person.class, "people"); flux.subscribe(testSubscriber); testSubscriber.awaitAndAssertNextValueCount(1); @@ -297,7 +289,7 @@ public class ReactiveMongoTemplateTests { p.setAge(22); template.insert(p).block(); - Query q1 = new Query(Criteria.where("id").is(p.getId())); + Query q1 = new Query(where("id").is(p.getId())); PersonWithAList p2 = template.findOne(q1, PersonWithAList.class).block(); assertThat(p2, notNullValue()); assertThat(p2.getWishList().size(), is(0)); @@ -345,7 +337,7 @@ public class ReactiveMongoTemplateTests { template.insert(p3).block(); // test query with a sort - Query q2 = new Query(Criteria.where("age").gt(10)); + Query q2 = new Query(where("age").gt(10)); q2.with(new Sort(Direction.DESC, "age")); PersonWithAList p5 = template.findOne(q2, PersonWithAList.class).block(); assertThat(p5.getFirstName(), is("Mark")); @@ -364,7 +356,7 @@ public class ReactiveMongoTemplateTests { person.setAge(25); mongoTemplate.insert(person).block(); - Query q = new Query(Criteria.where("BOGUS").gt(22)); + Query q = new Query(where("BOGUS").gt(22)); Update u = new Update().set("firstName", "Sven"); mongoTemplate.updateFirst(q, u, Person.class).block(); } @@ -377,9 +369,8 @@ public class ReactiveMongoTemplateTests { Person person = new Person("Oliver2", 25); template.insert(person) // - .then(template.updateFirst(new Query(Criteria.where("age").is(25)), new Update().set("firstName", "Sven"), - Person.class)) // - .flatMap(p -> template.find(new Query(Criteria.where("age").is(25)), Person.class)) + .then(template.updateFirst(new Query(where("age").is(25)), new Update().set("firstName", "Sven"), Person.class)) // + .flatMap(p -> template.find(new Query(where("age").is(25)), Person.class)) .subscribeWith(TestSubscriber.create()) // .await() // .assertValuesWith(result -> { @@ -395,9 +386,8 @@ public class ReactiveMongoTemplateTests { Person person = new Person("Oliver2", 25); template.insert(person, "people") // - .then(template.updateFirst(new Query(Criteria.where("age").is(25)), new Update().set("firstName", "Sven"), - "people")) // - .flatMap(p -> template.find(new Query(Criteria.where("age").is(25)), Person.class, "people")) + .then(template.updateFirst(new Query(where("age").is(25)), new Update().set("firstName", "Sven"), "people")) // + .flatMap(p -> template.find(new Query(where("age").is(25)), Person.class, "people")) .subscribeWith(TestSubscriber.create()) // .await() // .assertValuesWith(result -> { @@ -411,13 +401,13 @@ public class ReactiveMongoTemplateTests { @Test public void updateMultiByEntityTypeShouldUpdateObjects() throws Exception { - Query query = new Query(new Criteria().orOperator(Criteria.where("firstName").is("Walter Jr"), - Criteria.where("firstName").is("Walter"))); + Query query = new Query( + new Criteria().orOperator(where("firstName").is("Walter Jr"), Criteria.where("firstName").is("Walter"))); template.insertAll(Flux.just(new Person("Walter", 50), new Person("Skyler", 43), new Person("Walter Jr", 16))) // .collectList() // .flatMap(a -> template.updateMulti(query, new Update().set("firstName", "Walt"), Person.class)) // - .flatMap(p -> template.find(new Query(Criteria.where("firstName").is("Walt")), Person.class)) // + .flatMap(p -> template.find(new Query(where("firstName").is("Walt")), Person.class)) // .subscribeWith(TestSubscriber.create()) // .awaitAndAssertNextValueCount(2); } @@ -428,14 +418,14 @@ public class ReactiveMongoTemplateTests { @Test public void updateMultiByCollectionNameShouldUpdateObject() throws Exception { - Query query = new Query(new Criteria().orOperator(Criteria.where("firstName").is("Walter Jr"), - Criteria.where("firstName").is("Walter"))); + Query query = new Query( + new Criteria().orOperator(where("firstName").is("Walter Jr"), Criteria.where("firstName").is("Walter"))); template .insert(Flux.just(new Person("Walter", 50), new Person("Skyler", 43), new Person("Walter Jr", 16)), "people") // .collectList() // .flatMap(a -> template.updateMulti(query, new Update().set("firstName", "Walt"), Person.class, "people")) // - .flatMap(p -> template.find(new Query(Criteria.where("firstName").is("Walt")), Person.class, "people")) // + .flatMap(p -> template.find(new Query(where("firstName").is("Walt")), Person.class, "people")) // .subscribeWith(TestSubscriber.create()) // .awaitAndAssertNextValueCount(2); } @@ -482,7 +472,7 @@ public class ReactiveMongoTemplateTests { thrown.expectMessage("age"); // thrown.expectMessage("failed"); - Query query = new Query(Criteria.where("firstName").is("Amol")); + Query query = new Query(where("firstName").is("Amol")); Update upd = new Update().push("age", 29); template.updateFirst(query, upd, Person.class).block(); } @@ -627,7 +617,7 @@ public class ReactiveMongoTemplateTests { @Test public void doesNotFailOnVersionInitForUnversionedEntity() { - org.bson.Document dbObject = new org.bson.Document(); + Document dbObject = new Document(); dbObject.put("firstName", "Oliver"); template.insert(dbObject, template.determineCollectionName(PersonWithVersionPropertyOfTypeInteger.class)); @@ -685,7 +675,7 @@ public class ReactiveMongoTemplateTests { @Test public void savesPlainDbObjectCorrectly() { - org.bson.Document dbObject = new org.bson.Document("foo", "bar"); + Document dbObject = new Document("foo", "bar"); template.save(dbObject, "collection").block(); assertThat(dbObject.containsKey("_id"), is(true)); @@ -697,10 +687,10 @@ public class ReactiveMongoTemplateTests { @Test(expected = InvalidDataAccessApiUsageException.class) public void rejectsPlainObjectWithOutExplicitCollection() { - org.bson.Document dbObject = new org.bson.Document("foo", "bar"); + Document dbObject = new Document("foo", "bar"); template.save(dbObject, "collection").block(); - template.findById(dbObject.get("_id"), org.bson.Document.class).block(); + template.findById(dbObject.get("_id"), Document.class).block(); } /** @@ -709,10 +699,10 @@ public class ReactiveMongoTemplateTests { @Test public void readsPlainDbObjectById() { - org.bson.Document dbObject = new org.bson.Document("foo", "bar"); + Document dbObject = new Document("foo", "bar"); template.save(dbObject, "collection").block(); - org.bson.Document result = template.findById(dbObject.get("_id"), org.bson.Document.class, "collection").block(); + Document result = template.findById(dbObject.get("_id"), Document.class, "collection").block(); assertThat(result.get("foo"), is(dbObject.get("foo"))); assertThat(result.get("_id"), is(dbObject.get("_id"))); } @@ -729,7 +719,8 @@ public class ReactiveMongoTemplateTests { new Venue("Maplewood, NJ", -74.2713, 40.73137)); template.insertAll(venues).blockLast(); - template.indexOps(Venue.class).ensureIndex(new GeospatialIndex("location").typed(GeoSpatialIndexType.GEO_2D)); + IndexOperationsAdapter.blocking(template.reactiveIndexOps(Venue.class)) + .ensureIndex(new GeospatialIndex("location").typed(GeoSpatialIndexType.GEO_2D)); NearQuery geoFar = NearQuery.near(-73, 40, Metrics.KILOMETERS).num(10).maxDistance(150, Metrics.KILOMETERS); @@ -931,11 +922,11 @@ public class ReactiveMongoTemplateTests { @Test public void savesJsonStringCorrectly() { - org.bson.Document dbObject = new org.bson.Document().append("first", "first").append("second", "second"); + Document dbObject = new Document().append("first", "first").append("second", "second"); template.save(dbObject, "collection").block(); - org.bson.Document result = template.findAll(org.bson.Document.class, "collection").next().block(); + Document result = template.findAll(Document.class, "collection").next().block(); assertThat(result.containsKey("first"), is(true)); } diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/index/IndexInfoUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/index/IndexInfoUnitTests.java index 2b9926c32..a4a8ba1d3 100644 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/index/IndexInfoUnitTests.java +++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/index/IndexInfoUnitTests.java @@ -36,7 +36,7 @@ public class IndexInfoUnitTests { IndexField fooField = IndexField.create("foo", Direction.ASC); IndexField barField = IndexField.create("bar", Direction.DESC); - IndexInfo info = new IndexInfo(Arrays.asList(fooField, barField), "myIndex", false, false, false, ""); + IndexInfo info = new IndexInfo(Arrays.asList(fooField, barField), "myIndex", false, false, ""); assertThat(info.isIndexForFields(Arrays.asList("foo", "bar")), is(true)); } } 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 index 46a976d71..86d8f0f31 100644 --- 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 @@ -37,7 +37,6 @@ import org.springframework.data.annotation.Id; import org.springframework.data.domain.Sort.Direction; 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.core.index.MongoPersistentEntityIndexResolver.IndexDefinitionHolder; import org.springframework.data.mongodb.core.mapping.Document; import org.springframework.data.mongodb.core.mapping.MongoMappingContext; @@ -64,8 +63,8 @@ public class MongoPersistentEntityIndexCreatorIntegrationTests { static final String SAMPLE_TYPE_COLLECTION_NAME = "sampleEntity"; static final String RECURSIVE_TYPE_COLLECTION_NAME = "recursiveGenericTypes"; - public static @ClassRule RuleChain rules = RuleChain.outerRule(MongoVersionRule.atLeast(new Version(2, 6))).around( - CleanMongoDB.indexes(Arrays.asList(SAMPLE_TYPE_COLLECTION_NAME, RECURSIVE_TYPE_COLLECTION_NAME))); + public static @ClassRule RuleChain rules = RuleChain.outerRule(MongoVersionRule.atLeast(new Version(2, 6))) + .around(CleanMongoDB.indexes(Arrays.asList(SAMPLE_TYPE_COLLECTION_NAME, RECURSIVE_TYPE_COLLECTION_NAME))); public @Rule ExpectedException expectedException = ExpectedException.none(); @@ -113,11 +112,11 @@ public class MongoPersistentEntityIndexCreatorIntegrationTests { MongoPersistentEntityIndexCreator indexCreator = new MongoPersistentEntityIndexCreator(new MongoMappingContext(), mongoTemplate); - indexCreator.createIndex(new IndexDefinitionHolder("dalinar.kohlin", new Index().named("stormlight") - .on("lastname", Direction.ASC).unique(), "datamongo-1125")); + indexCreator.createIndex(new IndexDefinitionHolder("dalinar.kohlin", + new Index().named("stormlight").on("lastname", Direction.ASC).unique(), "datamongo-1125")); - indexCreator.createIndex(new IndexDefinitionHolder("dalinar.kohlin", new Index().named("stormlight") - .on("lastname", Direction.ASC).sparse(), "datamongo-1125")); + indexCreator.createIndex(new IndexDefinitionHolder("dalinar.kohlin", + new Index().named("stormlight").on("lastname", Direction.ASC).sparse(), "datamongo-1125")); } @Document(collection = RECURSIVE_TYPE_COLLECTION_NAME) diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/performance/ReactivePerformanceTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/performance/ReactivePerformanceTests.java index aeee6fd4f..e5dee0659 100644 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/performance/ReactivePerformanceTests.java +++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/performance/ReactivePerformanceTests.java @@ -18,8 +18,6 @@ package org.springframework.data.mongodb.performance; import static org.springframework.data.mongodb.core.query.Criteria.*; import static org.springframework.data.mongodb.core.query.Query.*; import static org.springframework.util.Assert.*; -import reactor.core.publisher.Flux; -import reactor.core.publisher.Mono; import java.text.DecimalFormat; import java.util.ArrayList; @@ -43,9 +41,9 @@ import org.junit.Test; import org.springframework.core.Constants; import org.springframework.core.convert.support.GenericConversionService; import org.springframework.data.annotation.PersistenceConstructor; -import org.springframework.data.mongodb.core.SimpleReactiveMongoDatabaseFactory; import org.springframework.data.mongodb.core.ReactiveMongoOperations; import org.springframework.data.mongodb.core.ReactiveMongoTemplate; +import org.springframework.data.mongodb.core.SimpleReactiveMongoDatabaseFactory; import org.springframework.data.mongodb.core.convert.DbRefProxyHandler; import org.springframework.data.mongodb.core.convert.DbRefResolver; import org.springframework.data.mongodb.core.convert.DbRefResolverCallback; @@ -70,11 +68,13 @@ import com.mongodb.reactivestreams.client.MongoClients; import com.mongodb.reactivestreams.client.MongoCollection; import com.mongodb.reactivestreams.client.MongoDatabase; +import reactor.core.publisher.Flux; +import reactor.core.publisher.Mono; /** - * Test class to execute performance tests for plain Reactive Streams MongoDB driver usage, {@link ReactiveMongoOperations} and the repositories - * abstraction. - * + * Test class to execute performance tests for plain Reactive Streams MongoDB driver usage, + * {@link ReactiveMongoOperations} and the repositories abstraction. + * * @author Mark Paluch */ public class ReactivePerformanceTests { @@ -96,22 +96,25 @@ public class ReactivePerformanceTests { @Before public void setUp() throws Exception { - this.mongo = MongoClients.create(); + mongo = MongoClients.create(); - SimpleReactiveMongoDatabaseFactory mongoDbFactory = new SimpleReactiveMongoDatabaseFactory(this.mongo, DATABASE_NAME); + SimpleReactiveMongoDatabaseFactory mongoDbFactory = new SimpleReactiveMongoDatabaseFactory(this.mongo, + DATABASE_NAME); MongoMappingContext context = new MongoMappingContext(); context.setInitialEntitySet(Collections.singleton(Person.class)); context.afterPropertiesSet(); - this.converter = new MappingMongoConverter(new DbRefResolver() { + converter = new MappingMongoConverter(new DbRefResolver() { @Override - public Object resolveDbRef(MongoPersistentProperty property, DBRef dbref, DbRefResolverCallback callback, DbRefProxyHandler proxyHandler) { + public Object resolveDbRef(MongoPersistentProperty property, DBRef dbref, DbRefResolverCallback callback, + DbRefProxyHandler proxyHandler) { return null; } @Override - public DBRef createDbRef(org.springframework.data.mongodb.core.mapping.DBRef annotation, MongoPersistentEntity entity, Object id) { + public DBRef createDbRef(org.springframework.data.mongodb.core.mapping.DBRef annotation, + MongoPersistentEntity entity, Object id) { return null; } @@ -125,12 +128,12 @@ public class ReactivePerformanceTests { return null; } }, context); - this.operations = new ReactiveMongoTemplate(mongoDbFactory, converter); + operations = new ReactiveMongoTemplate(mongoDbFactory, converter); ReactiveMongoRepositoryFactory factory = new ReactiveMongoRepositoryFactory(operations); factory.setConversionService(new GenericConversionService()); - this.repository = factory.getRepository(ReactivePersonRepository.class); + repository = factory.getRepository(ReactivePersonRepository.class); } @@ -139,24 +142,23 @@ public class ReactivePerformanceTests { */ @Test public void writeWithWriteConcerns() { - executeWithWriteConcerns(new WriteConcernCallback() { - public void doWithWriteConcern(String constantName, WriteConcern concern) { - writeHeadline("WriteConcern: " + constantName); - System.out.println(String.format("Writing %s objects using plain driver took %sms", NUMBER_OF_PERSONS, - writingObjectsUsingPlainDriver(NUMBER_OF_PERSONS, concern))); - System.out.println(String.format("Writing %s objects using template took %sms", NUMBER_OF_PERSONS, - writingObjectsUsingMongoTemplate(NUMBER_OF_PERSONS, concern))); - System.out.println(String.format("Writing %s objects using repository took %sms", NUMBER_OF_PERSONS, - writingObjectsUsingRepositories(NUMBER_OF_PERSONS, concern))); - - System.out.println(String.format("Writing %s objects async using plain driver took %sms", NUMBER_OF_PERSONS, - writingAsyncObjectsUsingPlainDriver(NUMBER_OF_PERSONS, concern))); - System.out.println(String.format("Writing %s objects async using template took %sms", NUMBER_OF_PERSONS, - writingAsyncObjectsUsingMongoTemplate(NUMBER_OF_PERSONS, concern))); - System.out.println(String.format("Writing %s objects async using repository took %sms", NUMBER_OF_PERSONS, - writingAsyncObjectsUsingRepositories(NUMBER_OF_PERSONS, concern))); - writeFooter(); - } + executeWithWriteConcerns((constantName, concern) -> { + + writeHeadline("WriteConcern: " + constantName); + System.out.println(String.format("Writing %s objects using plain driver took %sms", NUMBER_OF_PERSONS, + writingObjectsUsingPlainDriver(NUMBER_OF_PERSONS, concern))); + System.out.println(String.format("Writing %s objects using template took %sms", NUMBER_OF_PERSONS, + writingObjectsUsingMongoTemplate(NUMBER_OF_PERSONS, concern))); + System.out.println(String.format("Writing %s objects using repository took %sms", NUMBER_OF_PERSONS, + writingObjectsUsingRepositories(NUMBER_OF_PERSONS, concern))); + + System.out.println(String.format("Writing %s objects async using plain driver took %sms", NUMBER_OF_PERSONS, + writingAsyncObjectsUsingPlainDriver(NUMBER_OF_PERSONS, concern))); + System.out.println(String.format("Writing %s objects async using template took %sms", NUMBER_OF_PERSONS, + writingAsyncObjectsUsingMongoTemplate(NUMBER_OF_PERSONS, concern))); + System.out.println(String.format("Writing %s objects async using repository took %sms", NUMBER_OF_PERSONS, + writingAsyncObjectsUsingRepositories(NUMBER_OF_PERSONS, concern))); + writeFooter(); }); } @@ -178,19 +180,15 @@ public class ReactivePerformanceTests { private long convertDirectly(final List dbObjects) { - executeWatched(new WatchCallback>() { + executeWatched(() -> { - @Override - public List doInWatch() { - - List persons = new ArrayList(); - - for (Document dbObject : dbObjects) { - persons.add(Person.from(new Document(dbObject))); - } + List persons = new ArrayList(); - return persons; + for (Document dbObject : dbObjects) { + persons.add(Person.from(new Document(dbObject))); } + + return persons; }); return watch.getLastTaskTimeMillis(); @@ -198,19 +196,15 @@ public class ReactivePerformanceTests { private long convertUsingConverter(final List dbObjects) { - executeWatched(new WatchCallback>() { - - @Override - public List doInWatch() { - - List persons = new ArrayList(); + executeWatched(() -> { - for (Document dbObject : dbObjects) { - persons.add(converter.read(Person.class, dbObject)); - } + List persons = new ArrayList(); - return persons; + for (Document dbObject : dbObjects) { + persons.add(converter.read(Person.class, dbObject)); } + + return persons; }); return watch.getLastTaskTimeMillis(); @@ -237,9 +231,12 @@ public class ReactivePerformanceTests { statistics.registerTime(Api.TEMPLATE, Mode.WRITE, writingObjectsUsingMongoTemplate(numberOfPersons, concern)); statistics.registerTime(Api.REPOSITORY, Mode.WRITE, writingObjectsUsingRepositories(numberOfPersons, concern)); - statistics.registerTime(Api.DRIVER, Mode.WRITE_ASYNC, writingAsyncObjectsUsingPlainDriver(numberOfPersons, concern)); - statistics.registerTime(Api.TEMPLATE, Mode.WRITE_ASYNC, writingAsyncObjectsUsingMongoTemplate(numberOfPersons, concern)); - statistics.registerTime(Api.REPOSITORY, Mode.WRITE_ASYNC, writingAsyncObjectsUsingRepositories(numberOfPersons, concern)); + statistics.registerTime(Api.DRIVER, Mode.WRITE_ASYNC, + writingAsyncObjectsUsingPlainDriver(numberOfPersons, concern)); + statistics.registerTime(Api.TEMPLATE, Mode.WRITE_ASYNC, + writingAsyncObjectsUsingMongoTemplate(numberOfPersons, concern)); + statistics.registerTime(Api.REPOSITORY, Mode.WRITE_ASYNC, + writingAsyncObjectsUsingRepositories(numberOfPersons, concern)); statistics.registerTime(Api.DRIVER, Mode.READ, readingUsingPlainDriver()); statistics.registerTime(Api.TEMPLATE, Mode.READ, readingUsingTemplate()); @@ -267,22 +264,16 @@ public class ReactivePerformanceTests { } private long queryUsingTemplate() { - executeWatched(new WatchCallback>() { - public List doInWatch() { - Query query = query(where("addresses.zipCode").regex(".*1.*")); - return operations.find(query, Person.class, "template").collectList().block(); - } + executeWatched(() -> { + Query query = query(where("addresses.zipCode").regex(".*1.*")); + return operations.find(query, Person.class, "template").collectList().block(); }); return watch.getLastTaskTimeMillis(); } private long queryUsingRepository() { - executeWatched(new WatchCallback>() { - public List doInWatch() { - return repository.findByAddressesZipCodeContaining("1").collectList().block(); - } - }); + executeWatched(() -> repository.findByAddressesZipCodeContaining("1").collectList().block()); return watch.getLastTaskTimeMillis(); } @@ -325,7 +316,8 @@ public class ReactivePerformanceTests { private long writingObjectsUsingPlainDriver(int numberOfPersons, WriteConcern concern) { - final MongoCollection collection = mongo.getDatabase(DATABASE_NAME).getCollection("driver").withWriteConcern(concern); + final MongoCollection collection = mongo.getDatabase(DATABASE_NAME).getCollection("driver") + .withWriteConcern(concern); final List persons = getPersonObjects(numberOfPersons); executeWatched(new WatchCallback() { @@ -372,17 +364,19 @@ public class ReactivePerformanceTests { return watch.getLastTaskTimeMillis(); } - - + private long writingAsyncObjectsUsingPlainDriver(int numberOfPersons, WriteConcern concern) { - final MongoCollection collection = mongo.getDatabase(DATABASE_NAME).getCollection("driver").withWriteConcern(concern); + final MongoCollection collection = mongo.getDatabase(DATABASE_NAME).getCollection("driver") + .withWriteConcern(concern); final List persons = getPersonObjects(numberOfPersons); executeWatched(new WatchCallback() { public Void doInWatch() { - Flux.from(collection.insertMany(persons.stream().map(person -> new Document(person.toDocument())).collect(Collectors.toList()))).then().block(); + Flux.from(collection + .insertMany(persons.stream().map(person -> new Document(person.toDocument())).collect(Collectors.toList()))) + .then().block(); return null; } }); @@ -408,7 +402,6 @@ public class ReactivePerformanceTests { final List persons = getPersonObjects(numberOfPersons); - executeWatched(new WatchCallback() { public Void doInWatch() { operations.setWriteConcern(concern); @@ -417,53 +410,38 @@ public class ReactivePerformanceTests { } }); - - return watch.getLastTaskTimeMillis(); } private long readingUsingPlainDriver() { - executeWatched(new WatchCallback>() { - public List doInWatch() { - return Flux.from(mongo.getDatabase(DATABASE_NAME).getCollection("driver").find()).map(Person::from).collectList().block(); - } - }); + executeWatched(() -> Flux.from(mongo.getDatabase(DATABASE_NAME).getCollection("driver").find()).map(Person::from) + .collectList().block()); return watch.getLastTaskTimeMillis(); } private long readingUsingTemplate() { - executeWatched(new WatchCallback>() { - public List doInWatch() { - return operations.findAll(Person.class, "template").collectList().block(); - } - }); + executeWatched(() -> operations.findAll(Person.class, "template").collectList().block()); return watch.getLastTaskTimeMillis(); } private long readingUsingRepository() { - executeWatched(new WatchCallback>() { - public List doInWatch() { - return repository.findAll().collectList().block(); - } - }); + executeWatched(() -> repository.findAll().collectList().block()); return watch.getLastTaskTimeMillis(); } private long queryUsingPlainDriver() { - executeWatched(new WatchCallback>() { - public List doInWatch() { + executeWatched(() -> { - MongoCollection collection = mongo.getDatabase(DATABASE_NAME).getCollection("driver"); + MongoCollection collection = mongo.getDatabase(DATABASE_NAME).getCollection("driver"); - Document regex = new Document("$regex", Pattern.compile(".*1.*")); - Document query = new Document("addresses.zipCode", regex); - return Flux.from(collection.find(query)).map(Person::from).collectList().block(); - } + Document regex = new Document("$regex", Pattern.compile(".*1.*")); + Document query = new Document("addresses.zipCode", regex); + return Flux.from(collection.find(query)).map(Person::from).collectList().block(); }); return watch.getLastTaskTimeMillis(); @@ -755,8 +733,7 @@ public class ReactivePerformanceTests { } enum Mode { - WRITE, READ, QUERY, - WRITE_ASYNC + WRITE, READ, QUERY, WRITE_ASYNC } private static class Statistics { diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/ReactiveMongoRepositoryTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/ReactiveMongoRepositoryTests.java index d54bc669c..703b4ac30 100644 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/ReactiveMongoRepositoryTests.java +++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/ReactiveMongoRepositoryTests.java @@ -57,6 +57,7 @@ import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; import lombok.NoArgsConstructor; +import org.springframework.util.ClassUtils; import reactor.core.Cancellation; import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; @@ -83,7 +84,7 @@ public class ReactiveMongoRepositoryTests implements BeanClassLoaderAware, BeanF @Override public void setBeanClassLoader(ClassLoader classLoader) { - this.classLoader = classLoader == null ? org.springframework.util.ClassUtils.getDefaultClassLoader() : classLoader; + this.classLoader = classLoader == null ? ClassUtils.getDefaultClassLoader() : classLoader; } @Override diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/ReactivePersonRepository.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/ReactivePersonRepository.java index 6d67f46b2..ceaf6cc3a 100644 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/ReactivePersonRepository.java +++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/ReactivePersonRepository.java @@ -20,14 +20,14 @@ import reactor.core.publisher.Flux; /** * Sample reactive repository managing {@link Person} entities. - * + * * @author Mark Paluch */ public interface ReactivePersonRepository extends ReactiveMongoRepository { /** * Returns all {@link Person}s with the given lastname. - * + * * @param lastname * @return */ diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/SimpleReactiveMongoRepositoryTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/SimpleReactiveMongoRepositoryTests.java index de74174cd..36391197b 100644 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/SimpleReactiveMongoRepositoryTests.java +++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/SimpleReactiveMongoRepositoryTests.java @@ -43,6 +43,7 @@ import org.springframework.data.mongodb.repository.support.SimpleReactiveMongoRe import org.springframework.data.repository.query.DefaultEvaluationContextProvider; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; +import org.springframework.util.ClassUtils; import lombok.Data; import lombok.NoArgsConstructor; @@ -70,7 +71,7 @@ public class SimpleReactiveMongoRepositoryTests implements BeanClassLoaderAware, @Override public void setBeanClassLoader(ClassLoader classLoader) { - this.classLoader = classLoader == null ? org.springframework.util.ClassUtils.getDefaultClassLoader() : classLoader; + this.classLoader = classLoader == null ? ClassUtils.getDefaultClassLoader() : classLoader; } @Override diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/query/ReactiveMongoQueryExecutionUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/query/ReactiveMongoQueryExecutionUnitTests.java index 018439fb5..2dee66a31 100644 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/query/ReactiveMongoQueryExecutionUnitTests.java +++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/query/ReactiveMongoQueryExecutionUnitTests.java @@ -46,7 +46,7 @@ import reactor.core.publisher.Flux; /** * Unit tests for {@link ReactiveMongoQueryExecution}. - * + * * @author Mark Paluch */ @RunWith(MockitoJUnitRunner.class) diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/query/ReactiveStringBasedMongoQueryUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/query/ReactiveStringBasedMongoQueryUnitTests.java index 2ef286585..3ec17a875 100644 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/query/ReactiveStringBasedMongoQueryUnitTests.java +++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/query/ReactiveStringBasedMongoQueryUnitTests.java @@ -56,7 +56,7 @@ import reactor.core.publisher.Mono; /** * Unit tests for {@link ReactiveStringBasedMongoQuery}. - * + * * @author Mark Paluch */ @RunWith(MockitoJUnitRunner.class) @@ -138,7 +138,8 @@ public class ReactiveStringBasedMongoQueryUnitTests { @Test public void shouldSupportFindByParameterizedCriteriaAndFields() throws Exception { - ConvertingParameterAccessor accessor = StubParameterAccessor.getAccessor(converter, new Document("firstname", "first").append("lastname", "last"), Collections.singletonMap("lastname", 1)); + ConvertingParameterAccessor accessor = StubParameterAccessor.getAccessor(converter, + new Document("firstname", "first").append("lastname", "last"), Collections.singletonMap("lastname", 1)); ReactiveStringBasedMongoQuery mongoQuery = createQueryForMethod("findByParameterizedCriteriaAndFields", Document.class, Map.class); diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/support/ReactivePageImplUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/support/ReactivePageImplUnitTests.java index 67e867a92..26f9147ae 100644 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/support/ReactivePageImplUnitTests.java +++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/support/ReactivePageImplUnitTests.java @@ -21,14 +21,13 @@ import static org.junit.Assert.*; import org.junit.Test; import org.springframework.data.domain.Page; import org.springframework.data.domain.PageRequest; -import org.springframework.data.mongodb.repository.support.ReactivePageImpl; import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; /** * Unit tests for {@link ReactivePageImpl}. - * + * * @author Mark Paluch */ public class ReactivePageImplUnitTests { @@ -57,14 +56,15 @@ public class ReactivePageImplUnitTests { assertThat(page.hasNext(), is(true)); assertThat(page.nextPageable(), is(new PageRequest(1, 1))); } - + /** * @see DATAMONGO-1444 */ @Test public void returnsContentBoundedByPageSize() { - Page page = new ReactivePageImpl<>(Flux.just(new Object(), new Object()), new PageRequest(0, 1), Mono.just(10L)); + Page page = new ReactivePageImpl<>(Flux.just(new Object(), new Object()), new PageRequest(0, 1), + Mono.just(10L)); assertThat(page.getContent(), hasSize(1)); assertThat(page.hasNext(), is(true)); diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/support/ReactiveSliceImplUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/support/ReactiveSliceImplUnitTests.java index c3c019ecc..b9c63519c 100644 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/support/ReactiveSliceImplUnitTests.java +++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/support/ReactiveSliceImplUnitTests.java @@ -26,7 +26,7 @@ import reactor.core.publisher.Flux; /** * Unit tests for {@link ReactiveSliceImpl}. - * + * * @author Mark Paluch */ public class ReactiveSliceImplUnitTests { diff --git a/src/main/asciidoc/reference/reactive-mongo-repositories.adoc b/src/main/asciidoc/reference/reactive-mongo-repositories.adoc index bce3e8d4b..9deec5622 100644 --- a/src/main/asciidoc/reference/reactive-mongo-repositories.adoc +++ b/src/main/asciidoc/reference/reactive-mongo-repositories.adoc @@ -11,7 +11,7 @@ This chapter will point out the specialties for reactive repository support for The reactive space offers various reactive composition libraries. The most common libraries are https://github.com/ReactiveX/RxJava[RxJava] and https://projectreactor.io/[Project Reactor]. -Spring Data MongoDB is built on top of the MongoDB Reactive Streams driver to provide maximal interoperability relying on the http://www.reactive-streams.org/[Reactive Streams] initiative. Static APIs such as `ReactiveMongoOperations` are provided by using Project Reactor's `Flux` and `Mono` types. Project Reactor offers various adapters to convert reactive wrapper types (`Flux` to `Observable` and vice versa) but conversion can easily clutter your code. +Spring Data MongoDB is built on top of the https://mongodb.github.io/mongo-java-driver-reactivestreams/[MongoDB Reactive Streams] driver to provide maximal interoperability relying on the http://www.reactive-streams.org/[Reactive Streams] initiative. Static APIs such as `ReactiveMongoOperations` are provided by using Project Reactor's `Flux` and `Mono` types. Project Reactor offers various adapters to convert reactive wrapper types (`Flux` to `Observable` and vice versa) but conversion can easily clutter your code. Spring Data's Repository abstraction is a dynamic API, mostly defined by you and your requirements, as you're declaring query methods. Reactive MongoDB repositories can be either implemented using RxJava or Project Reactor wrapper types by simply extending from one of the library-specific repository interfaces: @@ -99,21 +99,17 @@ As our domain repository extends `ReactivePagingAndSortingRepository` it provide ==== [source,java] ---- -@RunWith(SpringJUnit4ClassRunner.class) -@ContextConfiguration public class PersonRepositoryTests { @Autowired ReactivePersonRepository repository; @Test public void readsFirstPageCorrectly() { - Mono> persons = repository.findAll(new PageRequest(0, 10)); } @Test public void readsFirstPageAsStream() { - Flux persons = repository.findAll(new PageRequest(0, 10)); } } @@ -136,7 +132,7 @@ Following features are supported: * <> * <> -Reactive Repositories do not support Type-safe Query methods using QueryDSL. +WARNING: Reactive Repositories do not support Type-safe Query methods using Querydsl. [[mongodb.reactive.repositories.queries.geo-spatial]] === Geo-spatial repository queries diff --git a/src/main/asciidoc/reference/reactive-mongodb.adoc b/src/main/asciidoc/reference/reactive-mongodb.adoc index 28ea856d5..76ea7e168 100644 --- a/src/main/asciidoc/reference/reactive-mongodb.adoc +++ b/src/main/asciidoc/reference/reactive-mongodb.adoc @@ -3,13 +3,13 @@ The reactive MongoDB support contains a basic set of features which are summarized below. -* Spring configuration support using Java based @Configuration classes a Mongo client instance and replica sets -* `ReactiveMongoTemplate` helper class that increases productivity using Mongo operations in a reactive manner. Includes integrated object mapping between documents and POJOs. -* Exception translation into Spring's portable Data Access Exception hierarchy -* Feature Rich Object Mapping integrated with Spring's Conversion Service -* Annotation based mapping metadata but extensible to support other metadata formats -* Persistence and mapping lifecycle events -* Java based Query, Criteria, and Update DSLs +* Spring configuration support using Java based `@Configuration` classes a `MongoClient` instance and replica sets. +* `ReactiveMongoTemplate` helper class that increases productivity using `MongoOperations in a reactive manner. Includes integrated object mapping between `Documents and POJOs. +* Exception translation into Spring's portable Data Access Exception hierarchy. +* Feature Rich Object Mapping integrated with Spring's `ConversionService`. +* Annotation based mapping metadata but extensible to support other metadata formats. +* Persistence and mapping lifecycle events. +* Java based `Query`, `Criteria`, and `Update` DSLs. * Automatic implementation of reactive Repository interfaces including support for custom finder methods. For most tasks you will find yourself using `ReactiveMongoTemplate` or the Repository support that both leverage the rich mapping functionality. `ReactiveMongoTemplate` is the place to look for accessing functionality such as incrementing counters or ad-hoc CRUD operations. `ReactiveMongoTemplate` also provides callback methods so that it is easy for you to get a hold of the low level API artifacts such as `MongoDatabase` to communicate directly with MongoDB. The goal with naming conventions on various API artifacts is to copy those in the base MongoDB Java driver so you can easily map your existing knowledge onto the Spring APIs. @@ -54,12 +54,11 @@ Then add the following to pom.xml dependencies section. NOTE: MongoDB uses two different drivers for blocking and reactive (non-blocking) data access. While blocking operations are provided by default, you're have to opt-in for reactive usage. -Create a simple Person class to persist: +Create a simple `Person` class to persist: [source,java] ---- -package org.spring.mongodb.example; - +@Document public class Person { private String id; @@ -92,17 +91,6 @@ And a main application to run [source,java] ---- -package org.spring.mongodb.example; - -import static org.springframework.data.mongodb.core.query.Criteria.*; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.data.mongodb.core.ReactiveMongoTemplate; -import org.springframework.data.mongodb.core.query.Query; - -import com.mongodb.reactivestreams.client.MongoClients; - public class ReactiveMongoApp { private static final Logger log = LoggerFactory.getLogger(ReactiveMongoApp.class); @@ -114,15 +102,10 @@ public class ReactiveMongoApp { ReactiveMongoTemplate mongoOps = new ReactiveMongoTemplate(MongoClients.create(), "database"); mongoOps.insert(new Person("Joe", 34)) - .flatMap(p -> mongoOps.findOne(new Query(where("name").is("Joe")), Person.class)) - .doOnNext(person -> log.info(person.toString())) - .flatMap(person -> mongoOps.dropCollection("person")) - .doOnComplete(latch::countDown) - .subscribe(); latch.await(); @@ -181,7 +164,7 @@ public class AppConfig { This approach allows you to use the standard `com.mongodb.reactivestreams.client.MongoClient` API that you may already be used to using. -An alternative is to register an instance of `com.mongodb.reactivestreams.client.MongoClient` instance with the container using Spring's `ReactiveMongoClientFactoryBean`. As compared to instantiating a `com.mongodb.reactivestreams.client.MongoClient` instance directly, the FactoryBean approach has the added advantage of also providing the container with an ExceptionTranslator implementation that translates MongoDB exceptions to exceptions in Spring's portable `DataAccessException` hierarchy for data access classes annotated with the `@Repository` annotation. This hierarchy and use of `@Repository` is described in http://docs.spring.io/spring/docs/{springVersion}/spring-framework-reference/html/dao.html[Spring's DAO support features]. +An alternative is to register an instance of `com.mongodb.reactivestreams.client.MongoClient` instance with the container using Spring's `ReactiveMongoClientFactoryBean`. As compared to instantiating a `com.mongodb.reactivestreams.client.MongoClient` instance directly, the FactoryBean approach has the added advantage of also providing the container with an `ExceptionTranslator` implementation that translates MongoDB exceptions to exceptions in Spring's portable `DataAccessException` hierarchy for data access classes annotated with the `@Repository` annotation. This hierarchy and use of `@Repository` is described in http://docs.spring.io/spring/docs/{springVersion}/spring-framework-reference/html/dao.html[Spring's DAO support features]. An example of a Java based bean metadata that supports exception translation on `@Repository` annotated classes is shown below: @@ -197,16 +180,16 @@ public class AppConfig { */ public @Bean ReactiveMongoClientFactoryBean mongoClient() { - ReactiveMongoClientFactoryBean mongoClient = new ReactiveMongoClientFactoryBean(); - mongoClient.setHost("localhost"); + ReactiveMongoClientFactoryBean clientFactory = new ReactiveMongoClientFactoryBean(); + clientFactory.setHost("localhost"); - return mongoClient; + return clientFactory; } } ---- ==== -To access the `com.mongodb.reactivestreams.client.MongoClient` object created by the `ReactiveMongoClientFactoryBean` in other `@Configuration` or your own classes, use a `private @Autowired MongoClient mongoClient;` field. +To access the `com.mongodb.reactivestreams.client.MongoClient` object created by the `ReactiveMongoClientFactoryBean` in other `@Configuration` or your own classes, just obtain the `MongoClient` from the context. [[mongo.mongo-db-factory]] @@ -246,7 +229,7 @@ public interface ReactiveMongoDatabaseFactory { The class `org.springframework.data.mongodb.core.SimpleReactiveMongoDatabaseFactory` provides implements the ReactiveMongoDatabaseFactory interface and is created with a standard `com.mongodb.reactivestreams.client.MongoClient` instance and the database name. -Instead of using the IoC container to create an instance of ReactiveMongoTemplate, you can just use them in standard Java code as shown below. +Instead of using the IoC container to create an instance of `ReactiveMongoTemplate`, you can just use them in standard Java code as shown below. [source,java] ---- @@ -256,27 +239,23 @@ public class MongoApp { public static void main(String[] args) throws Exception { - ReactiveMongoOperations mongoOps = new ReactiveMongoOperations(*new SimpleReactiveMongoDatabaseFactory(MongoClient.create(), "database")*); + ReactiveMongoOperations mongoOps = new ReactiveMongoOperations(new SimpleReactiveMongoDatabaseFactory(MongoClient.create(), "database")); mongoOps.insert(new Person("Joe", 34)) - .flatMap(p -> mongoOps.findOne(new Query(where("name").is("Joe")), Person.class)) - .doOnNext(person -> log.info(person.toString())) - .flatMap(person -> mongoOps.dropCollection("person")) - .subscribe(); } } ---- -The code in bold highlights the use of SimpleMongoDbFactory and is the only difference between the listing shown in the <>. +The use of `SimpleMongoDbFactory` is the only difference between the listing shown in the <>. [[mongo.mongo-db-factory-java]] === Registering a ReactiveMongoDatabaseFactory instance using Java based metadata -To register a ReactiveMongoDatabaseFactory instance with the container, you write code much like what was highlighted in the previous code listing. A simple example is shown below +To register a `ReactiveMongoDatabaseFactory` instance with the container, you write code much like what was highlighted in the previous code listing. A simple example is shown below [source,java] ---- @@ -315,7 +294,7 @@ NOTE: Once configured, `ReactiveMongoTemplate` is thread-safe and can be reused The mapping between MongoDB documents and domain classes is done by delegating to an implementation of the interface `MongoConverter`. Spring provides a default implementation with `MongoMappingConverter`, but you can also write your own converter. Please refer to the section on MongoConverters for more detailed information. -The `ReactiveMongoTemplate` class implements the interface `ReactiveMongoOperations`. In as much as possible, the methods on `ReactiveMongoOperations` are named after methods available on the MongoDB driver `Collection` object as as to make the API familiar to existing MongoDB developers who are used to the driver API. For example, you will find methods such as "find", "findAndModify", "findOne", "insert", "remove", "save", "update" and "updateMulti". The design goal was to make it as easy as possible to transition between the use of the base MongoDB driver and `ReactiveMongoOperations`. A major difference in between the two APIs is that ReactiveMongoOperations can be passed domain objects instead of `Document` and there are fluent APIs for `Query`, `Criteria`, and `Update` operations instead of populating a `Document` to specify the parameters for those operations. +The `ReactiveMongoTemplate` class implements the interface `ReactiveMongoOperations`. In as much as possible, the methods on `ReactiveMongoOperations` are named after methods available on the MongoDB driver `Collection` object as as to make the API familiar to existing MongoDB developers who are used to the driver API. For example, you will find methods such as "find", "findAndModify", "findOne", "insert", "remove", "save", "update" and "updateMulti". The design goal was to make it as easy as possible to transition between the use of the base MongoDB driver and `ReactiveMongoOperations`. A major difference in between the two APIs is that `ReactiveMongoOperations` can be passed domain objects instead of `Document` and there are fluent APIs for `Query`, `Criteria`, and `Update` operations instead of populating a `Document` to specify the parameters for those operations. NOTE: The preferred way to reference the operations on `ReactiveMongoTemplate` instance is via its interface `ReactiveMongoOperations`. @@ -350,7 +329,7 @@ public class AppConfig { ---- ==== -There are several overloaded constructors of ReactiveMongoTemplate. These are +There are several overloaded constructors of `ReactiveMongoTemplate`. These are * `ReactiveMongoTemplate(MongoClient mongo, String databaseName)` - takes the `com.mongodb.Mongo` object and the default database name to operate against. * `ReactiveMongoTemplate(ReactiveMongoDatabaseFactory mongoDatabaseFactory)` - takes a ReactiveMongoDatabaseFactory object that encapsulated the `com.mongodb.reactivestreams.client.MongoClient` object and database name. @@ -364,13 +343,13 @@ NOTE: The preferred way to reference the operations on `ReactiveMongoTemplate` i [[mongo.reactive.template.writeresultchecking]] === WriteResultChecking Policy -When in development it is very handy to either log or throw an exception if the `com.mongodb.WriteResult` returned from any MongoDB operation contains an error. It is quite common to forget to do this during development and then end up with an application that looks like it runs successfully but in fact the database was not modified according to your expectations. Set MongoTemplate's property to an enum with the following values, `LOG`, `EXCEPTION`, or `NONE` to either log the error, throw and exception or do nothing. The default is to use a `WriteResultChecking` value of `NONE`. +When in development it is very handy to either log or throw an `Exception` if the `com.mongodb.WriteResult` returned from any MongoDB operation contains an error. It is quite common to forget to do this during development and then end up with an application that looks like it runs successfully but in fact the database was not modified according to your expectations. Set MongoTemplate's property to an enum with the following values, `LOG`, `EXCEPTION`, or `NONE` to either log the error, throw and exception or do nothing. The default is to use a `WriteResultChecking` value of `NONE`. [[mongo.reactive.template.writeconcern]] === WriteConcern -You can set the `com.mongodb.WriteConcern` property that the `ReactiveMongoTemplate` will use for write operations if it has not yet been specified via the driver at a higher level such as `MongoDatabase`. If ReactiveMongoTemplate's `WriteConcern` property is not set it will default to the one set in the MongoDB driver's DB or Collection setting. +You can set the `com.mongodb.WriteConcern` property that the `ReactiveMongoTemplate` will use for write operations if it has not yet been specified via the driver at a higher level such as `MongoDatabase`. If ReactiveMongoTemplate's `WriteConcern` property is not set it will default to the one set in the MongoDB driver's `MongoDatabase` or `MongoCollection` setting. [[mongo.reactive.template.writeconcernresolver]] @@ -445,17 +424,6 @@ You can save, update and delete the object as shown below. [source,java] ---- -package org.spring.mongodb.example; - -import static org.springframework.data.mongodb.core.query.Criteria.*; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.data.mongodb.core.ReactiveMongoTemplate; -import org.springframework.data.mongodb.core.query.Query; - -import com.mongodb.reactivestreams.client.MongoClients; - public class ReactiveMongoApp { private static final Logger log = LoggerFactory.getLogger(ReactiveMongoApp.class); @@ -467,15 +435,10 @@ public class ReactiveMongoApp { ReactiveMongoTemplate mongoOps = new ReactiveMongoTemplate(MongoClients.create(), "database"); mongoOps.insert(new Person("Joe", 34)).doOnNext(person -> log.info("Insert: " + person)) - .flatMap(person -> mongoOps.findById(person.getId(), Person.class)) - .doOnNext(person -> log.info("Found: " + person)) - .zipWith(person -> mongoOps.updateFirst(query(where("name").is("Joe")), update("age", 35), Person.class)) - .flatMap(tuple -> mongoOps.remove(tuple.getT1())).flatMap(deleteResult -> mongoOps.findAll(Person.class)) - .count().doOnSuccess(count -> { log.info("Number of people: " + count); latch.countDown(); @@ -529,14 +492,9 @@ Here is an example that uses the `ReactiveCollectionCallback` to return informat [source,java] ---- -Flux hasIndex = template.execute("geolocation", collection -> { - - List indexes = template.indexOps(collection.getNamespace().getCollectionName()).getIndexInfo(); - for (IndexInfo dbo : indexes) { - if ("location_2d".equals(dbo.getName())) { - return Mono.just(true); - } - } - return Mono.just(false); -}); +Flux hasIndex = operations.execute("geolocation", + collection -> Flux.from(collection.listIndexes(Document.class)) + .filter(document -> document.get("name").equals("fancy-index-name")) + .flatMap(document -> Mono.just(true)) + .defaultIfEmpty(false)); ----