From e0a12d77f717292122a15ac6f7b572d080123d0b Mon Sep 17 00:00:00 2001 From: Christoph Strobl Date: Wed, 23 Jan 2019 10:31:12 +0100 Subject: [PATCH] DATAMONGO-2182 - Polishing. Introduce base class for common Querydsl query execution tasks that can be used for imperative and reactive implementation. Fix test issues due to context caching where indices are not recreated when dropping the collection during test setup. Update reference documentation. Original Pull Request: #635 --- .../QuerydslMongoPredicateExecutor.java | 47 ++------- .../QuerydslPredicateExecutorSupport.java | 92 ++++++++++++++++++ ...eactiveQuerydslMongoPredicateExecutor.java | 53 ++-------- .../ReactiveSpringDataMongodbQuery.java | 32 +++---- .../ReactiveMongoRepositoryTests.java | 96 +++++++++++++------ ...eQuerydslMongoPredicateExecutorTests.java} | 49 +++++++++- .../mongodb/test/util/MongoTestUtils.java | 18 ++++ .../reactive-mongo-repositories.adoc | 24 +++-- 8 files changed, 270 insertions(+), 141 deletions(-) create mode 100644 spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/support/QuerydslPredicateExecutorSupport.java rename spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/support/{ReactiveQuerydslMongoPredicateExecutorIntegrationTests.java => ReactiveQuerydslMongoPredicateExecutorTests.java} (86%) diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/support/QuerydslMongoPredicateExecutor.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/support/QuerydslMongoPredicateExecutor.java index 7e8bffc4f..08961f17b 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/support/QuerydslMongoPredicateExecutor.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/support/QuerydslMongoPredicateExecutor.java @@ -22,23 +22,18 @@ import org.springframework.dao.IncorrectResultSizeDataAccessException; import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Sort; -import org.springframework.data.domain.Sort.Order; import org.springframework.data.mongodb.core.MongoOperations; import org.springframework.data.mongodb.repository.query.MongoEntityInformation; import org.springframework.data.querydsl.EntityPathResolver; -import org.springframework.data.querydsl.QSort; import org.springframework.data.querydsl.QuerydslPredicateExecutor; import org.springframework.data.querydsl.SimpleEntityPathResolver; -import org.springframework.data.repository.core.EntityInformation; import org.springframework.data.repository.support.PageableExecutionUtils; import org.springframework.util.Assert; import com.querydsl.core.NonUniqueResultException; import com.querydsl.core.types.EntityPath; -import com.querydsl.core.types.Expression; import com.querydsl.core.types.OrderSpecifier; import com.querydsl.core.types.Predicate; -import com.querydsl.core.types.dsl.PathBuilder; /** * MongoDB-specific {@link QuerydslPredicateExecutor} that allows execution {@link Predicate}s in various forms. @@ -50,10 +45,9 @@ import com.querydsl.core.types.dsl.PathBuilder; * @author Mark Paluch * @since 2.0 */ -public class QuerydslMongoPredicateExecutor implements QuerydslPredicateExecutor { +public class QuerydslMongoPredicateExecutor extends QuerydslPredicateExecutorSupport + implements QuerydslPredicateExecutor { - private final PathBuilder builder; - private final EntityInformation entityInformation; private final MongoOperations mongoOperations; /** @@ -80,12 +74,8 @@ public class QuerydslMongoPredicateExecutor implements QuerydslPredicateExecu public QuerydslMongoPredicateExecutor(MongoEntityInformation entityInformation, MongoOperations mongoOperations, EntityPathResolver resolver) { - Assert.notNull(resolver, "EntityPathResolver must not be null!"); - - EntityPath path = resolver.createPath(entityInformation.getJavaType()); - - this.builder = new PathBuilder(path.getType(), path.getMetadata()); - this.entityInformation = entityInformation; + super(mongoOperations.getConverter(), pathBuilderFor(resolver.createPath(entityInformation.getJavaType())), + entityInformation); this.mongoOperations = mongoOperations; } @@ -210,7 +200,7 @@ public class QuerydslMongoPredicateExecutor implements QuerydslPredicateExecu * @return */ private SpringDataMongodbQuery createQuery() { - return new SpringDataMongodbQuery<>(mongoOperations, entityInformation.getJavaType()); + return new SpringDataMongodbQuery<>(mongoOperations, typeInformation().getJavaType()); } /** @@ -235,32 +225,7 @@ public class QuerydslMongoPredicateExecutor implements QuerydslPredicateExecu */ private SpringDataMongodbQuery applySorting(SpringDataMongodbQuery query, Sort sort) { - // TODO: find better solution than instanceof check - if (sort instanceof QSort) { - - List> orderSpecifiers = ((QSort) sort).getOrderSpecifiers(); - query.orderBy(orderSpecifiers.toArray(new OrderSpecifier[orderSpecifiers.size()])); - - return query; - } - - sort.stream().map(this::toOrder).forEach(query::orderBy); - + toOrderSpecifiers(sort).forEach(query::orderBy); return query; } - - /** - * Transforms a plain {@link Order} into a Querydsl specific {@link OrderSpecifier}. - * - * @param order - * @return - */ - @SuppressWarnings({ "rawtypes", "unchecked" }) - private OrderSpecifier toOrder(Order order) { - - Expression property = builder.get(order.getProperty()); - - return new OrderSpecifier( - order.isAscending() ? com.querydsl.core.types.Order.ASC : com.querydsl.core.types.Order.DESC, property); - } } diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/support/QuerydslPredicateExecutorSupport.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/support/QuerydslPredicateExecutorSupport.java new file mode 100644 index 000000000..30bfbf38b --- /dev/null +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/support/QuerydslPredicateExecutorSupport.java @@ -0,0 +1,92 @@ +/* + * Copyright 2019 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.data.mongodb.repository.support; + +import java.util.List; +import java.util.stream.Collectors; + +import org.springframework.data.domain.Sort; +import org.springframework.data.domain.Sort.Order; +import org.springframework.data.mongodb.core.convert.MongoConverter; +import org.springframework.data.querydsl.QSort; +import org.springframework.data.repository.core.EntityInformation; + +import com.querydsl.core.types.EntityPath; +import com.querydsl.core.types.Expression; +import com.querydsl.core.types.OrderSpecifier; +import com.querydsl.core.types.dsl.PathBuilder; + +/** + * @author Christoph Strobl + * @since 2.2 + */ +abstract class QuerydslPredicateExecutorSupport { + + private final SpringDataMongodbSerializer serializer; + private final PathBuilder builder; + private final EntityInformation entityInformation; + + QuerydslPredicateExecutorSupport(MongoConverter converter, PathBuilder builder, + EntityInformation entityInformation) { + + this.serializer = new SpringDataMongodbSerializer(converter); + this.builder = builder; + this.entityInformation = entityInformation; + } + + protected static PathBuilder pathBuilderFor(EntityPath path) { + return new PathBuilder<>(path.getType(), path.getMetadata()); + } + + protected EntityInformation typeInformation() { + return entityInformation; + } + + protected SpringDataMongodbSerializer mongodbSerializer() { + return serializer; + } + + /** + * Transforms a plain {@link Order} into a Querydsl specific {@link OrderSpecifier}. + * + * @param order + * @return + */ + @SuppressWarnings({ "rawtypes", "unchecked" }) + protected OrderSpecifier toOrder(Order order) { + + Expression property = builder.get(order.getProperty()); + + return new OrderSpecifier( + order.isAscending() ? com.querydsl.core.types.Order.ASC : com.querydsl.core.types.Order.DESC, property); + } + + /** + * Converts the given {@link Sort} to {@link OrderSpecifier}. + * + * @param sort + * @return + */ + protected List> toOrderSpecifiers(Sort sort) { + + if (sort instanceof QSort) { + return ((QSort) sort).getOrderSpecifiers(); + } + + return sort.stream().map(this::toOrder).collect(Collectors.toList()); + } + +} diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/support/ReactiveQuerydslMongoPredicateExecutor.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/support/ReactiveQuerydslMongoPredicateExecutor.java index 807696c05..547551639 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/support/ReactiveQuerydslMongoPredicateExecutor.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/support/ReactiveQuerydslMongoPredicateExecutor.java @@ -18,36 +18,29 @@ package org.springframework.data.mongodb.repository.support; import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; -import java.util.List; - import org.springframework.data.domain.Sort; -import org.springframework.data.domain.Sort.Order; import org.springframework.data.mongodb.core.ReactiveMongoOperations; import org.springframework.data.mongodb.repository.query.MongoEntityInformation; import org.springframework.data.querydsl.EntityPathResolver; -import org.springframework.data.querydsl.QSort; import org.springframework.data.querydsl.QuerydslPredicateExecutor; import org.springframework.data.querydsl.ReactiveQuerydslPredicateExecutor; import org.springframework.data.querydsl.SimpleEntityPathResolver; -import org.springframework.data.repository.core.EntityInformation; import org.springframework.util.Assert; import com.querydsl.core.types.EntityPath; -import com.querydsl.core.types.Expression; import com.querydsl.core.types.OrderSpecifier; import com.querydsl.core.types.Predicate; -import com.querydsl.core.types.dsl.PathBuilder; /** * MongoDB-specific {@link QuerydslPredicateExecutor} that allows execution {@link Predicate}s in various forms. * * @author Mark Paluch + * @author Christoph Strobl * @since 2.2 */ -public class ReactiveQuerydslMongoPredicateExecutor implements ReactiveQuerydslPredicateExecutor { +public class ReactiveQuerydslMongoPredicateExecutor extends QuerydslPredicateExecutorSupport + implements ReactiveQuerydslPredicateExecutor { - private final PathBuilder builder; - private final EntityInformation entityInformation; private final ReactiveMongoOperations mongoOperations; /** @@ -60,6 +53,7 @@ public class ReactiveQuerydslMongoPredicateExecutor implements ReactiveQueryd */ public ReactiveQuerydslMongoPredicateExecutor(MongoEntityInformation entityInformation, ReactiveMongoOperations mongoOperations) { + this(entityInformation, mongoOperations, SimpleEntityPathResolver.INSTANCE); } @@ -74,12 +68,8 @@ public class ReactiveQuerydslMongoPredicateExecutor implements ReactiveQueryd public ReactiveQuerydslMongoPredicateExecutor(MongoEntityInformation entityInformation, ReactiveMongoOperations mongoOperations, EntityPathResolver resolver) { - Assert.notNull(resolver, "EntityPathResolver must not be null!"); - - EntityPath path = resolver.createPath(entityInformation.getJavaType()); - - this.builder = new PathBuilder(path.getType(), path.getMetadata()); - this.entityInformation = entityInformation; + super(mongoOperations.getConverter(), pathBuilderFor(resolver.createPath(entityInformation.getJavaType())), + entityInformation); this.mongoOperations = mongoOperations; } @@ -185,10 +175,9 @@ public class ReactiveQuerydslMongoPredicateExecutor implements ReactiveQueryd * @return */ private ReactiveSpringDataMongodbQuery createQuery() { - SpringDataMongodbSerializer serializer = new SpringDataMongodbSerializer(mongoOperations.getConverter()); - Class javaType = entityInformation.getJavaType(); - return new ReactiveSpringDataMongodbQuery<>(serializer, mongoOperations, javaType, + Class javaType = typeInformation().getJavaType(); + return new ReactiveSpringDataMongodbQuery<>(mongodbSerializer(), mongoOperations, javaType, mongoOperations.getCollectionName(javaType)); } @@ -201,32 +190,8 @@ public class ReactiveQuerydslMongoPredicateExecutor implements ReactiveQueryd */ private ReactiveSpringDataMongodbQuery applySorting(ReactiveSpringDataMongodbQuery query, Sort sort) { - // TODO: find better solution than instanceof check - if (sort instanceof QSort) { - - List> orderSpecifiers = ((QSort) sort).getOrderSpecifiers(); - query.orderBy(orderSpecifiers.toArray(new OrderSpecifier[orderSpecifiers.size()])); - - return query; - } - - sort.stream().map(this::toOrder).forEach(query::orderBy); - + toOrderSpecifiers(sort).forEach(query::orderBy); return query; } - /** - * Transforms a plain {@link Order} into a Querydsl specific {@link OrderSpecifier}. - * - * @param order - * @return - */ - @SuppressWarnings({ "rawtypes", "unchecked" }) - private OrderSpecifier toOrder(Order order) { - - Expression property = builder.get(order.getProperty()); - - return new OrderSpecifier( - order.isAscending() ? com.querydsl.core.types.Order.ASC : com.querydsl.core.types.Order.DESC, property); - } } diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/support/ReactiveSpringDataMongodbQuery.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/support/ReactiveSpringDataMongodbQuery.java index 575139330..ea5d0a2b9 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/support/ReactiveSpringDataMongodbQuery.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/support/ReactiveSpringDataMongodbQuery.java @@ -29,6 +29,7 @@ import org.springframework.data.mongodb.core.query.Query; import org.springframework.lang.Nullable; import org.springframework.util.LinkedMultiValueMap; import org.springframework.util.MultiValueMap; +import org.springframework.util.StringUtils; import com.querydsl.core.JoinExpression; import com.querydsl.core.QueryMetadata; @@ -45,8 +46,8 @@ import com.querydsl.core.types.dsl.CollectionPathBase; * MongoDB query with utilizing {@link ReactiveMongoOperations} for command execution. * * @param result type - * @param concrete subtype * @author Mark Paluch + * @author Christoph Strobl * @since 2.2 */ class ReactiveSpringDataMongodbQuery extends QuerydslAbstractMongodbQuery> { @@ -56,22 +57,18 @@ class ReactiveSpringDataMongodbQuery extends QuerydslAbstractMongodbQuery find; ReactiveSpringDataMongodbQuery(ReactiveMongoOperations mongoOperations, Class entityClass) { - - super(new SpringDataMongodbSerializer(mongoOperations.getConverter())); - - this.entityClass = (Class) entityClass; - this.mongoOperations = mongoOperations; - this.find = mongoOperations.query(this.entityClass); + this(new SpringDataMongodbSerializer(mongoOperations.getConverter()), mongoOperations, entityClass, null); } ReactiveSpringDataMongodbQuery(MongodbDocumentSerializer serializer, ReactiveMongoOperations mongoOperations, - Class entityClass, String collection) { + Class entityClass, @Nullable String collection) { super(serializer); this.entityClass = (Class) entityClass; this.mongoOperations = mongoOperations; - this.find = mongoOperations.query(this.entityClass).inCollection(collection); + this.find = StringUtils.hasText(collection) ? mongoOperations.query(this.entityClass).inCollection(collection) + : mongoOperations.query(this.entityClass); } /** @@ -79,7 +76,7 @@ class ReactiveSpringDataMongodbQuery extends QuerydslAbstractMongodbQuery fetch() { + Flux fetch() { return createQuery().flatMapMany(it -> find.matching(it).all()); } @@ -89,7 +86,7 @@ class ReactiveSpringDataMongodbQuery extends QuerydslAbstractMongodbQuery fetchOne() { + Mono fetchOne() { return createQuery().flatMap(it -> find.matching(it).one()); } @@ -98,7 +95,7 @@ class ReactiveSpringDataMongodbQuery extends QuerydslAbstractMongodbQuery fetchCount() { + Mono fetchCount() { return createQuery().flatMap(it -> find.matching(it).count()); } @@ -109,7 +106,7 @@ class ReactiveSpringDataMongodbQuery extends QuerydslAbstractMongodbQuery QuerydslJoinBuilder, K, T> join(Path ref, Path target) { + QuerydslJoinBuilder, K, T> join(Path ref, Path target) { return new QuerydslJoinBuilder<>(getQueryMixin(), ref, target); } @@ -120,8 +117,9 @@ class ReactiveSpringDataMongodbQuery extends QuerydslAbstractMongodbQuery QuerydslJoinBuilder, K, T> join(CollectionPathBase ref, + QuerydslJoinBuilder, K, T> join(CollectionPathBase ref, Path target) { + return new QuerydslJoinBuilder<>(getQueryMixin(), ref, target); } @@ -132,8 +130,9 @@ class ReactiveSpringDataMongodbQuery extends QuerydslAbstractMongodbQuery QuerydslAnyEmbeddedBuilder, K> anyEmbedded( + QuerydslAnyEmbeddedBuilder, K> anyEmbedded( Path> collection, Path target) { + return new QuerydslAnyEmbeddedBuilder<>(getQueryMixin(), collection); } @@ -259,6 +258,7 @@ class ReactiveSpringDataMongodbQuery extends QuerydslAbstractMongodbQuery getIds(Class targetType, @Nullable Predicate condition) { + return createQuery(Mono.justOrEmpty(condition), null, QueryModifiers.EMPTY, Collections.emptyList()) .flatMapMany(query -> mongoOperations.findDistinct(query, "_id", targetType, Object.class)); } @@ -270,7 +270,7 @@ class ReactiveSpringDataMongodbQuery extends QuerydslAbstractMongodbQuery source; - public NoMatchException(Path source) { + NoMatchException(Path source) { this.source = source; } 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 bdcc0dafe..6243c35e1 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 @@ -15,7 +15,7 @@ */ package org.springframework.data.mongodb.repository; -import static org.assertj.core.api.Assertions.*; +import static org.assertj.core.api.Assertions.offset; import static org.springframework.data.domain.Sort.Direction.*; import static org.springframework.data.mongodb.test.util.Assertions.assertThat; @@ -32,14 +32,14 @@ import java.util.concurrent.LinkedBlockingDeque; import java.util.concurrent.TimeUnit; import org.junit.Before; +import org.junit.BeforeClass; import org.junit.Test; import org.junit.runner.RunWith; import org.reactivestreams.Publisher; -import org.springframework.beans.BeansException; -import org.springframework.beans.factory.BeanClassLoaderAware; import org.springframework.beans.factory.BeanFactory; -import org.springframework.beans.factory.BeanFactoryAware; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; import org.springframework.dao.IncorrectResultSizeDataAccessException; import org.springframework.data.domain.PageRequest; import org.springframework.data.domain.Pageable; @@ -50,18 +50,23 @@ import org.springframework.data.geo.Distance; import org.springframework.data.geo.GeoResult; import org.springframework.data.geo.Metrics; import org.springframework.data.geo.Point; +import org.springframework.data.mongodb.config.AbstractReactiveMongoConfiguration; import org.springframework.data.mongodb.core.CollectionOptions; +import org.springframework.data.mongodb.core.ReactiveMongoOperations; import org.springframework.data.mongodb.core.ReactiveMongoTemplate; import org.springframework.data.mongodb.core.mapping.Document; import org.springframework.data.mongodb.repository.Person.Sex; import org.springframework.data.mongodb.repository.support.ReactiveMongoRepositoryFactory; import org.springframework.data.mongodb.repository.support.SimpleReactiveMongoRepository; +import org.springframework.data.mongodb.test.util.MongoTestUtils; import org.springframework.data.querydsl.ReactiveQuerydslPredicateExecutor; import org.springframework.data.repository.Repository; import org.springframework.data.repository.query.QueryMethodEvaluationContextProvider; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; -import org.springframework.util.ClassUtils; + +import com.mongodb.reactivestreams.client.MongoClient; +import com.mongodb.reactivestreams.client.MongoClients; /** * Test for {@link ReactiveMongoRepository} query methods. @@ -70,45 +75,74 @@ import org.springframework.util.ClassUtils; * @author Christoph Strobl */ @RunWith(SpringJUnit4ClassRunner.class) -@ContextConfiguration("classpath:reactive-infrastructure.xml") -public class ReactiveMongoRepositoryTests implements BeanClassLoaderAware, BeanFactoryAware { +@ContextConfiguration +public class ReactiveMongoRepositoryTests { @Autowired ReactiveMongoTemplate template; - ReactiveMongoRepositoryFactory factory; - ClassLoader classLoader; - BeanFactory beanFactory; - ReactivePersonRepository repository; - ReactiveContactRepository contactRepository; - ReactiveCappedCollectionRepository cappedRepository; + @Autowired ReactivePersonRepository repository; + @Autowired ReactiveContactRepository contactRepository; + @Autowired ReactiveCappedCollectionRepository cappedRepository; Person dave, oliver, carter, boyd, stefan, leroi, alicia; - QPerson person = new QPerson("person"); + QPerson person = QPerson.person; - @Override - public void setBeanClassLoader(ClassLoader classLoader) { - this.classLoader = classLoader == null ? ClassUtils.getDefaultClassLoader() : classLoader; + @Configuration + static class Config extends AbstractReactiveMongoConfiguration { + + @Bean + @Override + public MongoClient reactiveMongoClient() { + return MongoClients.create(); + } + + @Override + protected String getDatabaseName() { + return "reactive"; + } + + @Bean + ReactiveMongoRepositoryFactory factory(ReactiveMongoOperations template, BeanFactory beanFactory) { + + ReactiveMongoRepositoryFactory factory = new ReactiveMongoRepositoryFactory(template); + factory.setRepositoryBaseClass(SimpleReactiveMongoRepository.class); + factory.setBeanClassLoader(beanFactory.getClass().getClassLoader()); + factory.setBeanFactory(beanFactory); + factory.setEvaluationContextProvider(QueryMethodEvaluationContextProvider.DEFAULT); + + return factory; + } + + @Bean + ReactivePersonRepository reactivePersonRepository(ReactiveMongoRepositoryFactory factory) { + return factory.getRepository(ReactivePersonRepository.class); + } + + @Bean + ReactiveContactRepository reactiveContactRepository(ReactiveMongoRepositoryFactory factory) { + return factory.getRepository(ReactiveContactRepository.class); + } + + @Bean + ReactiveCappedCollectionRepository reactiveCappedCollectionRepository(ReactiveMongoRepositoryFactory factory) { + return factory.getRepository(ReactiveCappedCollectionRepository.class); + } } - @Override - public void setBeanFactory(BeanFactory beanFactory) throws BeansException { - this.beanFactory = beanFactory; + @BeforeClass + public static void cleanDb() { + + try (MongoClient client = MongoClients.create()) { + + MongoTestUtils.createOrReplaceCollectionNow("reactive", "person", client); + MongoTestUtils.createOrReplaceCollectionNow("reactive", "capped", client); + } } @Before public void setUp() throws Exception { - factory = new ReactiveMongoRepositoryFactory(template); - factory.setRepositoryBaseClass(SimpleReactiveMongoRepository.class); - factory.setBeanClassLoader(classLoader); - factory.setBeanFactory(beanFactory); - factory.setEvaluationContextProvider(QueryMethodEvaluationContextProvider.DEFAULT); - - repository = factory.getRepository(ReactivePersonRepository.class); - contactRepository = factory.getRepository(ReactiveContactRepository.class); - cappedRepository = factory.getRepository(ReactiveCappedCollectionRepository.class); - - StepVerifier.create(repository.deleteAll()).verifyComplete(); + repository.deleteAll().as(StepVerifier::create).verifyComplete(); dave = new Person("Dave", "Matthews", 42); oliver = new Person("Oliver August", "Matthews", 4); diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/support/ReactiveQuerydslMongoPredicateExecutorIntegrationTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/support/ReactiveQuerydslMongoPredicateExecutorTests.java similarity index 86% rename from spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/support/ReactiveQuerydslMongoPredicateExecutorIntegrationTests.java rename to spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/support/ReactiveQuerydslMongoPredicateExecutorTests.java index 2fd94afe0..0ba60c8f9 100644 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/support/ReactiveQuerydslMongoPredicateExecutorIntegrationTests.java +++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/support/ReactiveQuerydslMongoPredicateExecutorTests.java @@ -22,14 +22,17 @@ import java.util.Arrays; import java.util.LinkedHashSet; import org.junit.Before; +import org.junit.BeforeClass; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Configuration; import org.springframework.dao.IncorrectResultSizeDataAccessException; import org.springframework.dao.PermissionDeniedDataAccessException; import org.springframework.data.domain.Sort; import org.springframework.data.domain.Sort.Direction; import org.springframework.data.mongodb.ReactiveMongoDatabaseFactory; +import org.springframework.data.mongodb.config.AbstractReactiveMongoConfiguration; import org.springframework.data.mongodb.core.ReactiveMongoOperations; import org.springframework.data.mongodb.core.ReactiveMongoTemplate; import org.springframework.data.mongodb.repository.Address; @@ -39,20 +42,24 @@ import org.springframework.data.mongodb.repository.QPerson; import org.springframework.data.mongodb.repository.QUser; import org.springframework.data.mongodb.repository.User; import org.springframework.data.mongodb.repository.query.MongoEntityInformation; +import org.springframework.data.mongodb.test.util.MongoTestUtils; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringRunner; import com.mongodb.MongoException; +import com.mongodb.reactivestreams.client.MongoClient; +import com.mongodb.reactivestreams.client.MongoClients; import com.mongodb.reactivestreams.client.MongoDatabase; /** * Tests for {@link ReactiveQuerydslMongoPredicateExecutor}. * * @author Mark Paluch + * @author Christoph Strobl */ @RunWith(SpringRunner.class) -@ContextConfiguration("classpath:reactive-infrastructure.xml") -public class ReactiveQuerydslMongoPredicateExecutorIntegrationTests { +@ContextConfiguration +public class ReactiveQuerydslMongoPredicateExecutorTests { @Autowired ReactiveMongoOperations operations; @Autowired ReactiveMongoDatabaseFactory dbFactory; @@ -62,6 +69,30 @@ public class ReactiveQuerydslMongoPredicateExecutorIntegrationTests { Person dave, oliver, carter; QPerson person; + @Configuration + static class Config extends AbstractReactiveMongoConfiguration { + + @Override + public MongoClient reactiveMongoClient() { + return MongoClients.create(); + } + + @Override + protected String getDatabaseName() { + return "reactive"; + } + } + + @BeforeClass + public static void cleanDb() { + + try (MongoClient client = MongoClients.create()) { + + MongoTestUtils.createOrReplaceCollectionNow("reactive", "person", client); + MongoTestUtils.createOrReplaceCollectionNow("reactive", "user", client); + } + } + @Before public void setup() { @@ -98,6 +129,20 @@ public class ReactiveQuerydslMongoPredicateExecutorIntegrationTests { .verifyComplete(); } + @Test // DATAMONGO-2182 + public void shouldSupportCountWithPredicate() { + + repository.count(person.firstname.eq("Dave")) // + .as(StepVerifier::create) // + .expectNext(1L) // + .verifyComplete(); + + repository.count(person.firstname.eq("Unknown")) // + .as(StepVerifier::create) // + .expectNext(0L) // + .verifyComplete(); + } + @Test // DATAMONGO-2182 public void shouldSupportFindAllWithPredicateAndSort() { diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/test/util/MongoTestUtils.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/test/util/MongoTestUtils.java index a0e834713..8bdf7e493 100644 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/test/util/MongoTestUtils.java +++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/test/util/MongoTestUtils.java @@ -16,6 +16,7 @@ package org.springframework.data.mongodb.test.util; import reactor.core.publisher.Mono; +import reactor.test.StepVerifier; import org.bson.Document; @@ -81,6 +82,23 @@ public class MongoTestUtils { .then(Mono.from(database.createCollection(collectionName))); } + /** + * Create a {@link com.mongodb.client.MongoCollection} if it does not exist, or drop and recreate it if it does and + * verify operation result. + * + * @param dbName must not be {@literal null}. + * @param collectionName must not be {@literal null}. + * @param client must not be {@literal null}. + */ + public static void createOrReplaceCollectionNow(String dbName, String collectionName, + com.mongodb.reactivestreams.client.MongoClient client) { + + createOrReplaceCollection(dbName, collectionName, client) // + .as(StepVerifier::create) // + .expectNext(Success.SUCCESS) // + .verifyComplete(); + } + /** * Create a new {@link com.mongodb.MongoClient} with defaults suitable for replica set usage. * diff --git a/src/main/asciidoc/reference/reactive-mongo-repositories.adoc b/src/main/asciidoc/reference/reactive-mongo-repositories.adoc index faa022e5d..2a94956bf 100644 --- a/src/main/asciidoc/reference/reactive-mongo-repositories.adoc +++ b/src/main/asciidoc/reference/reactive-mongo-repositories.adoc @@ -132,8 +132,6 @@ It supports the following features: * <> * <> -WARNING: Reactive Repositories do not support type-safe query methods that use `Querydsl`. - [[mongodb.reactive.repositories.queries.geo-spatial]] === Geo-spatial Repository Queries @@ -203,7 +201,12 @@ public interface PersonRepository extends ReactiveMongoRepository result = repository.findAll(person.address.zipCode.eq("C0123")); ---- -`QPerson` is a class that is generated by the Java annotation post-processing tool. It is a `Predicate` that lets you write type-safe queries. Notice that there are no strings in the query other than the `C0123` value. +`QPerson` is a class that is generated by the Java annotation post-processing tool. It is a `Predicate` that lets you write type-safe queries. +Note that there are no strings in the query other than the `C0123` value. You can use the generated `Predicate` class by using the `ReactiveQuerydslPredicateExecutor` interface, which the following listing shows: +.The Gateway to Reactive Querydsl - The ReactiveQuerydslPredicateExecutor +==== [source,java] ---- public interface ReactiveQuerydslPredicateExecutor { @@ -245,9 +251,12 @@ public interface ReactiveQuerydslPredicateExecutor { Mono exists(Predicate predicate); } ---- +==== To use this in your repository implementation, add it to the list of repository interfaces from which your interface inherits, as the following example shows: +.Reactive Querydsl Respository Declaration +==== [source,java] ---- public interface PersonRepository extends ReactiveMongoRepository, ReactiveQuerydslPredicateExecutor { @@ -255,5 +264,6 @@ public interface PersonRepository extends ReactiveMongoRepository