diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/Aggregation.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/Aggregation.java
index 29ad75f14..42cd40c75 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/Aggregation.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/Aggregation.java
@@ -44,6 +44,7 @@ import org.springframework.data.mongodb.core.annotation.Collation;
@Target({ ElementType.METHOD, ElementType.ANNOTATION_TYPE })
@Documented
@QueryAnnotation
+@ReadPreference
public @interface Aggregation {
/**
@@ -127,4 +128,21 @@ public @interface Aggregation {
*/
@AliasFor(annotation = Collation.class, attribute = "value")
String collation() default "";
+
+ /**
+ * The mode of the read preference to use.
+ * {@code @Aggregation(pipeline = { ... }, readPreference = "secondary")} can be used as shortcut for:
+ *
+ *
+ * @@Aggregation(pipeline = { ... })
+ * @ReadPreference("secondary")
+ * List<PersonAggregate> groupByLastnameAnd(String property);
+ *
+ *
+ * @return the index name.
+ * @since 4.2
+ * @see ReadPreference#value()
+ */
+ @AliasFor(annotation = ReadPreference.class, attribute = "value")
+ String readPreference() default "";
}
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/Query.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/Query.java
index a3928e40a..05d741ddd 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/Query.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/Query.java
@@ -157,7 +157,7 @@ public @interface Query {
*
* @Query(...)
* @ReadPreference("secondary")
- * List<User> findAllByLastname(String collation);
+ * List<User> findAllByLastname(String lastname);
*
*
* @return the index name.
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/ReadPreference.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/ReadPreference.java
index 2ba4e32fd..0def67a22 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/ReadPreference.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/ReadPreference.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2011-2023 the original author or authors.
+ * Copyright 2023 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.
@@ -25,6 +25,7 @@ import java.lang.annotation.Target;
* Annotation to declare read preference for repository and query.
*
* @author Jorge RodrÃguez
+ * @author Christoph Strobl
* @since 4.2
*/
@Retention(RetentionPolicy.RUNTIME)
@@ -38,16 +39,5 @@ public @interface ReadPreference {
*/
String value() default "";
- /**
- * Set read preference tags
- * @return read preference tags
- */
- ReadPreferenceTag[] tags() default {};
-
- /**
- * Set read preference maxStalenessSeconds
- * @return read preference maxStalenessSeconds
- */
- long maxStalenessSeconds() default -1;
}
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/ReadPreferenceTag.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/ReadPreferenceTag.java
deleted file mode 100644
index 5fae877ee..000000000
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/ReadPreferenceTag.java
+++ /dev/null
@@ -1,37 +0,0 @@
-/*
- * Copyright 2011-2023 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
- *
- * https://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;
-
-/**
- * Annotation used by {@link ReadPreference} for define {@link com.mongodb.Tag}
- *
- * @author Jorge RodrÃguez
- * @since 4.2
- */
-public @interface ReadPreferenceTag {
-
- /**
- * Set the name of tag
- * @return name of tag
- */
- String name();
-
- /**
- * Set the value of tag
- * @return value of tag
- */
- String value();
-}
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/AbstractMongoQuery.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/AbstractMongoQuery.java
index 0913a9fb4..b1e0b3647 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/AbstractMongoQuery.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/AbstractMongoQuery.java
@@ -159,8 +159,8 @@ public abstract class AbstractMongoQuery implements RepositoryQuery {
if (!method.hasAnnotatedReadPreference()) {
return query;
}
-
- return query.withReadPreference(method.getAnnotatedReadPreference());
+
+ return query.withReadPreference(com.mongodb.ReadPreference.valueOf(method.getAnnotatedReadPreference()));
}
private MongoQueryExecution getExecution(ConvertingParameterAccessor accessor, FindWithQuery> operation) {
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 51301697e..d074a3803 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
@@ -164,7 +164,6 @@ public abstract class AbstractReactiveMongoQuery implements RepositoryQuery {
query = applyHintIfPresent(query);
query = applyAnnotatedReadPreferenceIfPresent(query);
-
FindWithQuery> find = typeToRead == null //
? findOperationWithProjection //
: findOperationWithProjection.as(typeToRead);
@@ -303,7 +302,7 @@ public abstract class AbstractReactiveMongoQuery implements RepositoryQuery {
return query;
}
- return query.withReadPreference(method.getAnnotatedReadPreference());
+ return query.withReadPreference(com.mongodb.ReadPreference.valueOf(method.getAnnotatedReadPreference()));
}
/**
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/AggregationUtils.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/AggregationUtils.java
index e57286f76..4e6390c5e 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/AggregationUtils.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/AggregationUtils.java
@@ -37,6 +37,8 @@ import org.springframework.lang.Nullable;
import org.springframework.util.ClassUtils;
import org.springframework.util.ObjectUtils;
+import com.mongodb.ReadPreference;
+
/**
* Internal utility class to help avoid duplicate code required in both the reactive and the sync {@link Aggregation}
* support offered by repositories.
@@ -117,6 +119,22 @@ abstract class AggregationUtils {
return builder.hint(queryMethod.getAnnotatedHint());
}
+ /**
+ * If present apply the preference from the {@link org.springframework.data.mongodb.repository.ReadPreference} annotation.
+ *
+ * @param builder must not be {@literal null}.
+ * @return never {@literal null}.
+ * @since 4.2
+ */
+ static AggregationOptions.Builder applyReadPreference(AggregationOptions.Builder builder, MongoQueryMethod queryMethod) {
+
+ if (!queryMethod.hasAnnotatedReadPreference()) {
+ return builder;
+ }
+
+ return builder.readPreference(ReadPreference.valueOf(queryMethod.getAnnotatedReadPreference()));
+ }
+
/**
* Append {@code $sort} aggregation stage if {@link ConvertingParameterAccessor#getSort()} is present.
*
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/MongoQueryMethod.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/MongoQueryMethod.java
index f4f30a5a9..68ba0a742 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/MongoQueryMethod.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/MongoQueryMethod.java
@@ -22,11 +22,7 @@ import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.Optional;
-import java.util.concurrent.TimeUnit;
-import java.util.stream.Collectors;
-import com.mongodb.Tag;
-import com.mongodb.TagSet;
import org.springframework.core.annotation.AnnotatedElementUtils;
import org.springframework.data.geo.GeoPage;
import org.springframework.data.geo.GeoResult;
@@ -322,7 +318,7 @@ public class MongoQueryMethod extends QueryMethod {
/**
- * Check if the query method is decorated with an non empty {@link Query#collation()}.
+ * Check if the query method is decorated with an non empty {@link ReadPreference}.
*
* @return true if method annotated with {@link Query} or {@link Aggregation} having a non-empty collation attribute.
* @since 4.2
@@ -334,44 +330,27 @@ public class MongoQueryMethod extends QueryMethod {
/**
* Get the {@link com.mongodb.ReadPreference} extracted from the {@link ReadPreference} annotation.
*
- * @return the {@link ReadPreference()}.
+ * @return the name of the {@link ReadPreference()}.
* @throws IllegalStateException if method not annotated with {@link Query}. Make sure to check
* {@link #hasAnnotatedQuery()} first.
* @since 4.2
*/
- public com.mongodb.ReadPreference getAnnotatedReadPreference() {
+ public String getAnnotatedReadPreference() {
- return doFindReadPreferenceAnnotation().map(annotationReadPreference -> {
-
- com.mongodb.ReadPreference readPreference = com.mongodb.ReadPreference.valueOf(annotationReadPreference.value());
-
- if (annotationReadPreference.tags().length > 0) {
- List tags = Arrays.stream(annotationReadPreference.tags())
- .map(tag -> new Tag(tag.name(), tag.value()))
- .collect(Collectors.toList());
- readPreference = readPreference.withTagSet(new TagSet(tags));
- }
-
- if (annotationReadPreference.maxStalenessSeconds() > 0) {
- readPreference = readPreference.withMaxStalenessMS(annotationReadPreference.maxStalenessSeconds(), TimeUnit.SECONDS);
- }
-
- return readPreference;
- }).orElseThrow(() -> new IllegalStateException(
+ return doFindReadPreferenceAnnotation().map(ReadPreference::value).orElseThrow(() -> new IllegalStateException(
"Expected to find @ReadPreference annotation but did not; Make sure to check hasAnnotatedReadPreference() before."));
}
/**
- * Get {@link com.mongodb.ReadPreference} from query. First check if the method is annotated. If not, check if the class is annotated.
+ * Get {@link com.mongodb.ReadPreference#getName() name} from query. First check if the method is annotated. If not, check if the class is annotated.
* So if the method and the class are annotated with @ReadPreference, the method annotation takes precedence.
- * @return the {@link com.mongodb.ReadPreference}
+ * @return the {@link ReadPreference}
* @since 4.2
*/
private Optional doFindReadPreferenceAnnotation() {
return doFindAnnotation(ReadPreference.class).or(() -> doFindAnnotationInClass(ReadPreference.class));
}
-
/**
* Check if the query method is decorated with an non empty {@link Query#collation()} or or
* {@link Aggregation#collation()}.
@@ -458,7 +437,6 @@ public class MongoQueryMethod extends QueryMethod {
@SuppressWarnings("unchecked")
private Optional doFindAnnotation(Class annotationType) {
-
return (Optional) this.annotationCache.computeIfAbsent(annotationType,
it -> Optional.ofNullable(AnnotatedElementUtils.findMergedAnnotation(method, it)));
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/ReactiveStringBasedAggregation.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/ReactiveStringBasedAggregation.java
index fd40440c9..adffbe5fe 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/ReactiveStringBasedAggregation.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/ReactiveStringBasedAggregation.java
@@ -132,6 +132,7 @@ public class ReactiveStringBasedAggregation extends AbstractReactiveMongoQuery {
expressionParser, evaluationContextProvider);
AggregationUtils.applyMeta(builder, method);
AggregationUtils.applyHint(builder, method);
+ AggregationUtils.applyReadPreference(builder, method);
TypeInformation> returnType = method.getReturnType();
if (returnType.getComponentType() != null) {
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/StringBasedAggregation.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/StringBasedAggregation.java
index 3f118d0fa..1a65e14db 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/StringBasedAggregation.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/StringBasedAggregation.java
@@ -181,6 +181,7 @@ public class StringBasedAggregation extends AbstractMongoQuery {
expressionParser, evaluationContextProvider);
AggregationUtils.applyMeta(builder, method);
AggregationUtils.applyHint(builder, method);
+ AggregationUtils.applyReadPreference(builder, method);
if (ReflectionUtils.isVoid(method.getReturnType().getType()) && pipeline.isOutOrMerge()) {
builder.skipOutput();
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/support/MongoRepositoryFactory.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/support/MongoRepositoryFactory.java
index 2d16be180..f768d2873 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/support/MongoRepositoryFactory.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/support/MongoRepositoryFactory.java
@@ -127,7 +127,7 @@ public class MongoRepositoryFactory extends RepositoryFactorySupport {
MongoEntityInformation, Serializable> entityInformation = getEntityInformation(information.getDomainType(),
information);
- return getTargetRepositoryViaReflection(information, entityInformation, operations);
+ return getTargetRepositoryViaReflection(information, information, entityInformation, operations);
}
@Override
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 120e1eff9..b8dd2cc99 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
@@ -114,7 +114,7 @@ public class ReactiveMongoRepositoryFactory extends ReactiveRepositoryFactorySup
MongoEntityInformation, Serializable> entityInformation = getEntityInformation(information.getDomainType(),
information);
- return getTargetRepositoryViaReflection(information, entityInformation, operations);
+ return getTargetRepositoryViaReflection(information, information, entityInformation, operations);
}
@Override
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/support/SimpleMongoRepository.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/support/SimpleMongoRepository.java
index 78cfd2c98..077c84291 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/support/SimpleMongoRepository.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/support/SimpleMongoRepository.java
@@ -27,6 +27,8 @@ import java.util.function.UnaryOperator;
import java.util.stream.Collectors;
import java.util.stream.Stream;
+import com.mongodb.ReadPreference;
+import org.springframework.core.annotation.AnnotatedElementUtils;
import org.springframework.dao.OptimisticLockingFailureException;
import org.springframework.data.domain.Example;
import org.springframework.data.domain.Page;
@@ -38,11 +40,14 @@ import org.springframework.data.domain.Sort;
import org.springframework.data.mongodb.core.ExecutableFindOperation;
import org.springframework.data.mongodb.core.MongoOperations;
import org.springframework.data.mongodb.core.MongoTemplate;
+import org.springframework.data.mongodb.core.ReadPreferenceAware;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.mongodb.repository.MongoRepository;
import org.springframework.data.mongodb.repository.query.MongoEntityInformation;
+import org.springframework.data.repository.core.RepositoryMetadata;
import org.springframework.data.support.PageableExecutionUtils;
+import org.springframework.data.util.Lazy;
import org.springframework.data.util.StreamUtils;
import org.springframework.data.util.Streamable;
import org.springframework.lang.Nullable;
@@ -62,9 +67,10 @@ import com.mongodb.client.result.DeleteResult;
*/
public class SimpleMongoRepository implements MongoRepository {
- private final MongoOperations mongoOperations;
+ private final @Nullable RepositoryMetadata repositoryMetadata;
private final MongoEntityInformation entityInformation;
-
+ private final MongoOperations mongoOperations;
+ private final Lazy readPreference;
/**
* Creates a new {@link SimpleMongoRepository} for the given {@link MongoEntityInformation} and {@link MongoTemplate}.
*
@@ -72,12 +78,34 @@ public class SimpleMongoRepository implements MongoRepository {
* @param mongoOperations must not be {@literal null}.
*/
public SimpleMongoRepository(MongoEntityInformation metadata, MongoOperations mongoOperations) {
+ this(null, metadata, mongoOperations);
+ }
+
+ /**
+ * Creates a new {@link SimpleMongoRepository} for the given {@link MongoEntityInformation} and {@link MongoTemplate}.
+ *
+ * @param repositoryMetadata
+ * @param metadata must not be {@literal null}.
+ * @param mongoOperations must not be {@literal null}.
+ * @since 4.2
+ */
+ public SimpleMongoRepository(@Nullable RepositoryMetadata repositoryMetadata, MongoEntityInformation metadata, MongoOperations mongoOperations) {
Assert.notNull(metadata, "MongoEntityInformation must not be null");
Assert.notNull(mongoOperations, "MongoOperations must not be null");
+ this.repositoryMetadata = repositoryMetadata;
this.entityInformation = metadata;
this.mongoOperations = mongoOperations;
+
+ this.readPreference = repositoryMetadata == null ? Lazy.empty() : Lazy.of(() -> {
+ org.springframework.data.mongodb.repository.ReadPreference preference = AnnotatedElementUtils.findMergedAnnotation(repositoryMetadata.getRepositoryInterface(), org.springframework.data.mongodb.repository.ReadPreference.class);
+ if (preference == null) {
+ return null;
+ }
+ return ReadPreference.valueOf(preference.value());
+ }
+ );
}
// -------------------------------------------------------------------------
@@ -213,7 +241,8 @@ public class SimpleMongoRepository implements MongoRepository {
Assert.notNull(sort, "Sort must not be null");
- return findAll(new Query().with(sort));
+ Query query = new Query().with(sort);
+ return findAll(query);
}
// -------------------------------------------------------------------------
@@ -253,6 +282,7 @@ public class SimpleMongoRepository implements MongoRepository {
Query query = new Query(new Criteria().alike(example)) //
.collation(entityInformation.getCollation());
+ readPreference.getOptional().ifPresent(query::withReadPreference);
return Optional
.ofNullable(mongoOperations.findOne(query, example.getProbeType(), entityInformation.getCollectionName()));
@@ -272,6 +302,7 @@ public class SimpleMongoRepository implements MongoRepository {
Query query = new Query(new Criteria().alike(example)) //
.collation(entityInformation.getCollation()) //
.with(sort);
+ readPreference.getOptional().ifPresent(query::withReadPreference);
return mongoOperations.find(query, example.getProbeType(), entityInformation.getCollectionName());
}
@@ -284,6 +315,7 @@ public class SimpleMongoRepository implements MongoRepository {
Query query = new Query(new Criteria().alike(example)) //
.collation(entityInformation.getCollation()).with(pageable); //
+ readPreference.getOptional().ifPresent(query::withReadPreference);
List list = mongoOperations.find(query, example.getProbeType(), entityInformation.getCollectionName());
@@ -337,7 +369,9 @@ public class SimpleMongoRepository implements MongoRepository {
private Query getIdQuery(Iterable extends ID> ids) {
- return new Query(new Criteria(entityInformation.getIdAttribute()).in(toCollection(ids)));
+ Query query = new Query(new Criteria(entityInformation.getIdAttribute()).in(toCollection(ids)));
+ readPreference.getOptional().ifPresent(query::withReadPreference);
+ return query;
}
private static Collection toCollection(Iterable ids) {
@@ -351,6 +385,7 @@ public class SimpleMongoRepository implements MongoRepository {
return Collections.emptyList();
}
+ readPreference.getOptional().ifPresent(query::withReadPreference);
return mongoOperations.find(query, entityInformation.getJavaType(), entityInformation.getCollectionName());
}
@@ -440,6 +475,8 @@ public class SimpleMongoRepository implements MongoRepository {
query.fields().include(getFieldsToInclude().toArray(new String[0]));
}
+ readPreference.getOptional().ifPresent(query::withReadPreference);
+
query = queryCustomizer.apply(query);
return mongoOperations.query(getPredicate().getProbeType()).inCollection(entityInformation.getCollectionName())
@@ -447,5 +484,4 @@ public class SimpleMongoRepository implements MongoRepository {
}
}
-
}
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 69b592468..f1db095f1 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
@@ -17,6 +17,12 @@ package org.springframework.data.mongodb.repository.support;
import static org.springframework.data.mongodb.core.query.Criteria.*;
+import com.mongodb.ReadPreference;
+import org.springframework.core.annotation.AnnotatedElementUtils;
+import org.springframework.data.mongodb.core.MongoTemplate;
+import org.springframework.data.repository.core.RepositoryMetadata;
+import org.springframework.data.util.Lazy;
+import org.springframework.lang.Nullable;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
@@ -63,17 +69,42 @@ import com.mongodb.client.result.DeleteResult;
*/
public class SimpleReactiveMongoRepository implements ReactiveMongoRepository {
+ private final @Nullable RepositoryMetadata repositoryMetadata;
private final MongoEntityInformation entityInformation;
private final ReactiveMongoOperations mongoOperations;
+ private final Lazy readPreference;
public SimpleReactiveMongoRepository(MongoEntityInformation entityInformation,
ReactiveMongoOperations mongoOperations) {
+ this(null, entityInformation, mongoOperations);
+ }
+
+ /**
+ * Creates a new {@link SimpleReactiveMongoRepository} for the given {@link MongoEntityInformation} and {@link MongoTemplate}.
+ *
+ * @param repositoryMetadata
+ * @param entityInformation must not be {@literal null}.
+ * @param mongoOperations must not be {@literal null}.
+ * @since 4.2
+ */
+ public SimpleReactiveMongoRepository(@Nullable RepositoryMetadata repositoryMetadata, MongoEntityInformation entityInformation,
+ ReactiveMongoOperations mongoOperations) {
Assert.notNull(entityInformation, "EntityInformation must not be null");
Assert.notNull(mongoOperations, "MongoOperations must not be null");
+ this.repositoryMetadata = repositoryMetadata;
this.entityInformation = entityInformation;
this.mongoOperations = mongoOperations;
+
+ this.readPreference = repositoryMetadata == null ? Lazy.empty() : Lazy.of(() -> {
+ org.springframework.data.mongodb.repository.ReadPreference preference = AnnotatedElementUtils.findMergedAnnotation(repositoryMetadata.getRepositoryInterface(), org.springframework.data.mongodb.repository.ReadPreference.class);
+ if (preference == null) {
+ return null;
+ }
+ return ReadPreference.valueOf(preference.value());
+ }
+ );
}
// -------------------------------------------------------------------------
@@ -312,6 +343,7 @@ public class SimpleReactiveMongoRepository implement
Query query = new Query(new Criteria().alike(example)) //
.collation(entityInformation.getCollation()) //
.limit(2);
+ readPreference.getOptional().ifPresent(query::withReadPreference);
return mongoOperations.find(query, example.getProbeType(), entityInformation.getCollectionName()).buffer(2)
.map(vals -> {
@@ -340,6 +372,7 @@ public class SimpleReactiveMongoRepository implement
Query query = new Query(new Criteria().alike(example)) //
.collation(entityInformation.getCollation()) //
.with(sort);
+ readPreference.getOptional().ifPresent(query::withReadPreference);
return mongoOperations.find(query, example.getProbeType(), entityInformation.getCollectionName());
}
@@ -394,6 +427,8 @@ public class SimpleReactiveMongoRepository implement
}
private Flux findAll(Query query) {
+
+ readPreference.getOptional().ifPresent(query::withReadPreference);
return mongoOperations.find(query, entityInformation.getJavaType(), entityInformation.getCollectionName());
}
@@ -479,6 +514,8 @@ public class SimpleReactiveMongoRepository implement
query.fields().include(getFieldsToInclude().toArray(new String[0]));
}
+ readPreference.getOptional().ifPresent(query::withReadPreference);
+
query = queryCustomizer.apply(query);
return mongoOperations.query(getPredicate().getProbeType()).inCollection(entityInformation.getCollectionName())
diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/query/AbstractMongoQueryUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/query/AbstractMongoQueryUnitTests.java
index 7c416f5ca..06b4522fc 100644
--- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/query/AbstractMongoQueryUnitTests.java
+++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/query/AbstractMongoQueryUnitTests.java
@@ -23,11 +23,7 @@ import java.lang.reflect.Method;
import java.util.List;
import java.util.Locale;
import java.util.Optional;
-import java.util.concurrent.TimeUnit;
-import com.mongodb.Tag;
-import com.mongodb.TagSet;
-import com.mongodb.TaggableReadPreference;
import org.bson.Document;
import org.bson.codecs.configuration.CodecRegistry;
import org.bson.types.ObjectId;
@@ -70,7 +66,6 @@ import org.springframework.data.mongodb.repository.Hint;
import org.springframework.data.mongodb.repository.Meta;
import org.springframework.data.mongodb.repository.MongoRepository;
import org.springframework.data.mongodb.repository.ReadPreference;
-import org.springframework.data.mongodb.repository.ReadPreferenceTag;
import org.springframework.data.mongodb.repository.Update;
import org.springframework.data.projection.ProjectionFactory;
import org.springframework.data.projection.SpelAwareProxyProjectionFactory;
@@ -532,10 +527,7 @@ class AbstractMongoQueryUnitTests {
ArgumentCaptor captor = ArgumentCaptor.forClass(Query.class);
verify(withQueryMock).matching(captor.capture());
- assertThat(captor.getValue().getReadPreference().getName()).isEqualTo("secondaryPreferred");
- assertThat(((TaggableReadPreference)captor.getValue().getReadPreference()).getTagSetList())
- .containsExactly(new TagSet(List.of(new Tag("local", "east"), new Tag("pre", "west"))));
- assertThat(((TaggableReadPreference)captor.getValue().getReadPreference()).getMaxStaleness(TimeUnit.SECONDS)).isEqualTo(99);
+ assertThat(captor.getValue().getReadPreference()).isEqualTo(com.mongodb.ReadPreference.secondaryPreferred());
}
@Test // GH-2971
@@ -545,10 +537,7 @@ class AbstractMongoQueryUnitTests {
ArgumentCaptor captor = ArgumentCaptor.forClass(Query.class);
verify(withQueryMock).matching(captor.capture());
- assertThat(captor.getValue().getReadPreference().getName()).isEqualTo("primaryPreferred");
- assertThat(((TaggableReadPreference)captor.getValue().getReadPreference()).getTagSetList())
- .containsExactly(new TagSet(List.of(new Tag("primary-local", "east"), new Tag("primary-pre", "west"))));
- assertThat(((TaggableReadPreference)captor.getValue().getReadPreference()).getMaxStaleness(TimeUnit.SECONDS)).isEqualTo(20);
+ assertThat(captor.getValue().getReadPreference()).isEqualTo(com.mongodb.ReadPreference.primaryPreferred());
}
@@ -622,11 +611,7 @@ class AbstractMongoQueryUnitTests {
}
}
- @ReadPreference(
- value = "primaryPreferred",
- tags = { @ReadPreferenceTag(name = "primary-local", value = "east"), @ReadPreferenceTag(name = "primary-pre", value = "west") },
- maxStalenessSeconds = 20
- )
+ @ReadPreference(value = "primaryPreferred")
private interface Repo extends MongoRepository {
List deleteByLastname(String lastname);
@@ -683,11 +668,7 @@ class AbstractMongoQueryUnitTests {
List findWithSortAndLimit(String firstname, Sort sort, Limit limit);
- @ReadPreference(
- value = "secondaryPreferred",
- tags = { @ReadPreferenceTag(name = "local", value = "east"), @ReadPreferenceTag(name = "pre", value = "west") },
- maxStalenessSeconds = 99
- )
+ @ReadPreference(value = "secondaryPreferred")
List findWithReadPreferenceByFirstname(String firstname);
}
diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/query/AbstractReactiveMongoQueryUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/query/AbstractReactiveMongoQueryUnitTests.java
index df882b642..c0c07cdc6 100644
--- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/query/AbstractReactiveMongoQueryUnitTests.java
+++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/query/AbstractReactiveMongoQueryUnitTests.java
@@ -18,29 +18,15 @@ package org.springframework.data.mongodb.repository.query;
import static org.assertj.core.api.Assertions.*;
import static org.mockito.Mockito.*;
-import com.mongodb.MongoClientSettings;
-import com.mongodb.Tag;
-import com.mongodb.TagSet;
-import com.mongodb.TaggableReadPreference;
-import com.mongodb.client.result.UpdateResult;
-import org.bson.codecs.configuration.CodecRegistry;
-import org.springframework.data.mongodb.core.ReactiveUpdateOperation.TerminatingUpdate;
-import org.springframework.data.mongodb.core.ReactiveUpdateOperation.ReactiveUpdate;
-import org.springframework.data.mongodb.core.ReactiveUpdateOperation.UpdateWithQuery;
-import org.springframework.data.mongodb.core.query.UpdateDefinition;
-import org.springframework.data.mongodb.repository.Hint;
-import org.springframework.data.mongodb.repository.ReadPreference;
-import org.springframework.data.mongodb.repository.ReadPreferenceTag;
-import org.springframework.data.mongodb.repository.Update;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import java.lang.reflect.Method;
import java.util.List;
import java.util.Locale;
-import java.util.concurrent.TimeUnit;
import org.bson.Document;
+import org.bson.codecs.configuration.CodecRegistry;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
@@ -50,11 +36,13 @@ import org.mockito.Mockito;
import org.mockito.junit.jupiter.MockitoExtension;
import org.mockito.junit.jupiter.MockitoSettings;
import org.mockito.quality.Strictness;
-
import org.springframework.data.mongodb.core.Person;
import org.springframework.data.mongodb.core.ReactiveFindOperation.FindWithQuery;
import org.springframework.data.mongodb.core.ReactiveFindOperation.ReactiveFind;
import org.springframework.data.mongodb.core.ReactiveMongoOperations;
+import org.springframework.data.mongodb.core.ReactiveUpdateOperation.ReactiveUpdate;
+import org.springframework.data.mongodb.core.ReactiveUpdateOperation.TerminatingUpdate;
+import org.springframework.data.mongodb.core.ReactiveUpdateOperation.UpdateWithQuery;
import org.springframework.data.mongodb.core.convert.MappingMongoConverter;
import org.springframework.data.mongodb.core.convert.NoOpDbRefResolver;
import org.springframework.data.mongodb.core.mapping.BasicMongoPersistentEntity;
@@ -62,13 +50,20 @@ import org.springframework.data.mongodb.core.mapping.MongoMappingContext;
import org.springframework.data.mongodb.core.query.BasicQuery;
import org.springframework.data.mongodb.core.query.Collation;
import org.springframework.data.mongodb.core.query.Query;
+import org.springframework.data.mongodb.core.query.UpdateDefinition;
+import org.springframework.data.mongodb.repository.Hint;
import org.springframework.data.mongodb.repository.ReactiveMongoRepository;
+import org.springframework.data.mongodb.repository.ReadPreference;
+import org.springframework.data.mongodb.repository.Update;
import org.springframework.data.projection.ProjectionFactory;
import org.springframework.data.projection.SpelAwareProxyProjectionFactory;
import org.springframework.data.repository.core.support.DefaultRepositoryMetadata;
import org.springframework.data.repository.query.ReactiveExtensionAwareQueryMethodEvaluationContextProvider;
import org.springframework.expression.spel.standard.SpelExpressionParser;
+import com.mongodb.MongoClientSettings;
+import com.mongodb.client.result.UpdateResult;
+
/**
* Unit tests for {@link AbstractReactiveMongoQuery}.
*
@@ -276,10 +271,7 @@ class AbstractReactiveMongoQueryUnitTests {
ArgumentCaptor captor = ArgumentCaptor.forClass(Query.class);
verify(withQueryMock).matching(captor.capture());
- assertThat(captor.getValue().getReadPreference().getName()).isEqualTo("secondaryPreferred");
- assertThat(((TaggableReadPreference)captor.getValue().getReadPreference()).getTagSetList())
- .containsExactly(new TagSet(List.of(new Tag("local", "east"), new Tag("pre", "west"))));
- assertThat(((TaggableReadPreference)captor.getValue().getReadPreference()).getMaxStaleness(TimeUnit.SECONDS)).isEqualTo(99);
+ assertThat(captor.getValue().getReadPreference()).isEqualTo(com.mongodb.ReadPreference.secondaryPreferred());
}
private ReactiveMongoQueryFake createQueryForMethod(String methodName, Class>... paramTypes) {
@@ -387,11 +379,7 @@ class AbstractReactiveMongoQueryUnitTests {
@Hint("idx-fn")
void findWithHintByFirstname(String firstname);
- @ReadPreference(
- value = "secondaryPreferred",
- tags = { @ReadPreferenceTag(name = "local", value = "east"), @ReadPreferenceTag(name = "pre", value = "west") },
- maxStalenessSeconds = 99
- )
+ @ReadPreference(value = "secondaryPreferred")
Flux findWithReadPreferenceByFirstname(String firstname);
}
}
diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/query/MongoQueryMethodUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/query/MongoQueryMethodUnitTests.java
index 53ae7a404..866391300 100644
--- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/query/MongoQueryMethodUnitTests.java
+++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/query/MongoQueryMethodUnitTests.java
@@ -319,7 +319,7 @@ public class MongoQueryMethodUnitTests {
MongoQueryMethod method = queryMethod(PersonRepository.class, "findWithReadPreferenceFromAtReadPreferenceByFirstname", String.class);
assertThat(method.hasAnnotatedReadPreference()).isTrue();
- assertThat(method.getAnnotatedReadPreference().getName()).isEqualTo("secondaryPreferred");
+ assertThat(method.getAnnotatedReadPreference()).isEqualTo("secondaryPreferred");
}
@Test // GH-2971
@@ -328,7 +328,7 @@ public class MongoQueryMethodUnitTests {
MongoQueryMethod method = queryMethod(PersonRepository.class, "findWithReadPreferenceFromAtQueryByFirstname", String.class);
assertThat(method.hasAnnotatedReadPreference()).isTrue();
- assertThat(method.getAnnotatedReadPreference().getName()).isEqualTo("secondaryPreferred");
+ assertThat(method.getAnnotatedReadPreference()).isEqualTo("secondaryPreferred");
}
@Test // GH-2971
@@ -337,8 +337,7 @@ public class MongoQueryMethodUnitTests {
MongoQueryMethod method = queryMethod(PersonRepository.class, "findWithMultipleReadPreferencesFromAtQueryAndAtReadPreferenceByFirstname", String.class);
assertThat(method.hasAnnotatedReadPreference()).isTrue();
- assertThat(method.getAnnotatedReadPreference().getName()).isEqualTo("secondaryPreferred");
-
+ assertThat(method.getAnnotatedReadPreference()).isEqualTo("secondaryPreferred");
}
@Test // GH-2971
@@ -347,7 +346,16 @@ public class MongoQueryMethodUnitTests {
MongoQueryMethod method = queryMethod(PersonRepository.class, "deleteByUserName", String.class);
assertThat(method.hasAnnotatedReadPreference()).isTrue();
- assertThat(method.getAnnotatedReadPreference().getName()).isEqualTo("primaryPreferred");
+ assertThat(method.getAnnotatedReadPreference()).isEqualTo("primaryPreferred");
+ }
+
+ @Test // GH-2971
+ void detectsReadPreferenceForAggregation() throws Exception {
+
+ MongoQueryMethod method = queryMethod(PersonRepository.class, "findByAggregationWithReadPreference");
+
+ assertThat(method.hasAnnotatedReadPreference()).isTrue();
+ assertThat(method.getAnnotatedReadPreference()).isEqualTo("secondaryPreferred");
}
private MongoQueryMethod queryMethod(Class> repository, String name, Class>... parameters) throws Exception {
@@ -406,6 +414,9 @@ public class MongoQueryMethodUnitTests {
collation = "de_AT")
List findByAggregationWithCollation();
+ @Aggregation(pipeline = "{'$group': { _id: '$templateId', maxVersion : { $max : '$version'} } }", readPreference = "secondaryPreferred")
+ List findByAggregationWithReadPreference();
+
void findAndUpdateBy(String firstname, Update update);
void findAndUpdateBy(String firstname, UpdateDefinition update);
diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/query/ReactiveMongoQueryMethodUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/query/ReactiveMongoQueryMethodUnitTests.java
index 6122da17b..0b52bcb44 100644
--- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/query/ReactiveMongoQueryMethodUnitTests.java
+++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/query/ReactiveMongoQueryMethodUnitTests.java
@@ -17,11 +17,7 @@ package org.springframework.data.mongodb.repository.query;
import static org.assertj.core.api.Assertions.*;
-import org.junit.jupiter.api.BeforeEach;
-import org.junit.jupiter.api.Test;
-import org.springframework.data.mongodb.core.annotation.Collation;
-import org.springframework.data.mongodb.repository.Query;
-import org.springframework.data.mongodb.repository.ReadPreference;
+import org.springframework.data.mongodb.repository.query.MongoQueryMethodUnitTests.PersonRepository;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
@@ -29,7 +25,8 @@ import java.lang.reflect.Method;
import java.util.List;
import org.assertj.core.api.Assertions;
-
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
import org.springframework.dao.InvalidDataAccessApiUsageException;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
@@ -38,12 +35,15 @@ import org.springframework.data.geo.Distance;
import org.springframework.data.geo.GeoResult;
import org.springframework.data.geo.Point;
import org.springframework.data.mongodb.core.User;
+import org.springframework.data.mongodb.core.annotation.Collation;
import org.springframework.data.mongodb.core.mapping.MongoMappingContext;
import org.springframework.data.mongodb.repository.Address;
import org.springframework.data.mongodb.repository.Aggregation;
import org.springframework.data.mongodb.repository.Contact;
import org.springframework.data.mongodb.repository.Meta;
import org.springframework.data.mongodb.repository.Person;
+import org.springframework.data.mongodb.repository.Query;
+import org.springframework.data.mongodb.repository.ReadPreference;
import org.springframework.data.projection.ProjectionFactory;
import org.springframework.data.projection.SpelAwareProxyProjectionFactory;
import org.springframework.data.repository.Repository;
@@ -222,7 +222,7 @@ public class ReactiveMongoQueryMethodUnitTests {
@Test // GH-3002
void annotatedCollationClashSelectsAtCollationAnnotationValue() throws Exception {
- ReactiveMongoQueryMethod method = queryMethod(MongoQueryMethodUnitTests.PersonRepository.class, "findWithMultipleCollationsFromAtQueryAndAtCollationByFirstname", String.class);
+ ReactiveMongoQueryMethod method = queryMethod(PersonRepository.class, "findWithMultipleCollationsFromAtQueryAndAtCollationByFirstname", String.class);
assertThat(method.hasAnnotatedCollation()).isTrue();
assertThat(method.getAnnotatedCollation()).isEqualTo("de_AT");
@@ -232,37 +232,46 @@ public class ReactiveMongoQueryMethodUnitTests {
@Test // GH-2971
void readsReadPreferenceAtQueryAnnotation() throws Exception {
- MongoQueryMethod method = queryMethod(MongoQueryMethodUnitTests.PersonRepository.class, "findWithReadPreferenceFromAtReadPreferenceByFirstname", String.class);
+ ReactiveMongoQueryMethod method = queryMethod(PersonRepository.class, "findWithReadPreferenceFromAtReadPreferenceByFirstname", String.class);
assertThat(method.hasAnnotatedReadPreference()).isTrue();
- assertThat(method.getAnnotatedReadPreference().getName()).isEqualTo("secondaryPreferred");
+ assertThat(method.getAnnotatedReadPreference()).isEqualTo("secondaryPreferred");
}
@Test // GH-2971
void readsReadPreferenceFromAtQueryAnnotation() throws Exception {
- MongoQueryMethod method = queryMethod(MongoQueryMethodUnitTests.PersonRepository.class, "findWithReadPreferenceFromAtQueryByFirstname", String.class);
+ ReactiveMongoQueryMethod method = queryMethod(PersonRepository.class, "findWithReadPreferenceFromAtQueryByFirstname", String.class);
assertThat(method.hasAnnotatedReadPreference()).isTrue();
- assertThat(method.getAnnotatedReadPreference().getName()).isEqualTo("secondaryPreferred");
+ assertThat(method.getAnnotatedReadPreference()).isEqualTo("secondaryPreferred");
}
@Test // GH-2971
void annotatedReadPreferenceClashSelectsAtReadPreferenceAnnotationValue() throws Exception {
- MongoQueryMethod method = queryMethod(MongoQueryMethodUnitTests.PersonRepository.class, "findWithMultipleReadPreferencesFromAtQueryAndAtReadPreferenceByFirstname", String.class);
+ ReactiveMongoQueryMethod method = queryMethod(PersonRepository.class, "findWithMultipleReadPreferencesFromAtQueryAndAtReadPreferenceByFirstname", String.class);
assertThat(method.hasAnnotatedReadPreference()).isTrue();
- assertThat(method.getAnnotatedReadPreference().getName()).isEqualTo("secondaryPreferred");
+ assertThat(method.getAnnotatedReadPreference()).isEqualTo("secondaryPreferred");
}
@Test // GH-2971
void readsReadPreferenceAtRepositoryAnnotation() throws Exception {
- MongoQueryMethod method = queryMethod(MongoQueryMethodUnitTests.PersonRepository.class, "deleteByUserName", String.class);
+ ReactiveMongoQueryMethod method = queryMethod(PersonRepository.class, "deleteByUserName", String.class);
assertThat(method.hasAnnotatedReadPreference()).isTrue();
- assertThat(method.getAnnotatedReadPreference().getName()).isEqualTo("primaryPreferred");
+ assertThat(method.getAnnotatedReadPreference()).isEqualTo("primaryPreferred");
+ }
+
+ @Test // GH-2971
+ void detectsReadPreferenceForAggregation() throws Exception {
+
+ ReactiveMongoQueryMethod method = queryMethod(MongoQueryMethodUnitTests.PersonRepository.class, "findByAggregationWithReadPreference");
+
+ assertThat(method.hasAnnotatedReadPreference()).isTrue();
+ assertThat(method.getAnnotatedReadPreference()).isEqualTo("secondaryPreferred");
}
private ReactiveMongoQueryMethod queryMethod(Class> repository, String name, Class>... parameters)
@@ -308,6 +317,9 @@ public class ReactiveMongoQueryMethodUnitTests {
collation = "de_AT")
Flux findByAggregationWithCollation();
+ @Aggregation(pipeline = "{'$group': { _id: '$templateId', maxVersion : { $max : '$version'} } }", readPreference = "secondaryPreferred")
+ Flux findByAggregationWithReadPreference();
+
@Collation("en_US")
List findWithCollationFromAtCollationByFirstname(String firstname);
diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/query/ReactiveStringBasedAggregationUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/query/ReactiveStringBasedAggregationUnitTests.java
index a54cb67db..c8f432fbd 100644
--- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/query/ReactiveStringBasedAggregationUnitTests.java
+++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/query/ReactiveStringBasedAggregationUnitTests.java
@@ -60,6 +60,8 @@ import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.lang.Nullable;
import org.springframework.util.ClassUtils;
+import com.mongodb.ReadPreference;
+
/**
* Unit tests for {@link ReactiveStringBasedAggregation}.
*
@@ -211,6 +213,13 @@ public class ReactiveStringBasedAggregationUnitTests {
assertThat(skipResultsOf(invocation)).isFalse();
}
+ @Test // GH-2971
+ void aggregatePicksUpReadPreferenceFromAnnotation() {
+
+ AggregationInvocation invocation = executeAggregation("withReadPreference");
+ assertThat(readPreferenceOf(invocation)).isEqualTo(ReadPreference.secondaryPreferred());
+ }
+
private ReactiveStringBasedAggregation createAggregationForMethod(String name, Class>... parameters) {
Method method = ClassUtils.getMethod(SampleRepository.class, name, parameters);
@@ -250,6 +259,12 @@ public class ReactiveStringBasedAggregationUnitTests {
: false;
}
+ @Nullable
+ private ReadPreference readPreferenceOf(AggregationInvocation invocation) {
+ return invocation.aggregation.getOptions() != null ? invocation.aggregation.getOptions().getReadPreference()
+ : null;
+ }
+
private Class> targetTypeOf(AggregationInvocation invocation) {
return invocation.getTargetType();
}
@@ -287,6 +302,9 @@ public class ReactiveStringBasedAggregationUnitTests {
@Aggregation(pipeline = { RAW_GROUP_BY_LASTNAME_STRING, RAW_OUT })
Mono outSkipResult();
+
+ @Aggregation(pipeline = { RAW_GROUP_BY_LASTNAME_STRING, RAW_OUT }, readPreference = "secondaryPreferred")
+ Mono withReadPreference();
}
static class PersonAggregate {
diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/query/StringBasedAggregationUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/query/StringBasedAggregationUnitTests.java
index 26fc04c2e..4c9b49947 100644
--- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/query/StringBasedAggregationUnitTests.java
+++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/query/StringBasedAggregationUnitTests.java
@@ -68,6 +68,7 @@ import org.springframework.lang.Nullable;
import org.springframework.util.ClassUtils;
import com.mongodb.MongoClientSettings;
+import com.mongodb.ReadPreference;
/**
* Unit tests for {@link StringBasedAggregation}.
@@ -282,6 +283,13 @@ public class StringBasedAggregationUnitTests {
assertThat(skipResultsOf(invocation)).isFalse();
}
+ @Test // GH-2971
+ void aggregatePicksUpReadPreferenceFromAnnotation() {
+
+ AggregationInvocation invocation = executeAggregation("withReadPreference");
+ assertThat(readPreferenceOf(invocation)).isEqualTo(ReadPreference.secondaryPreferred());
+ }
+
private AggregationInvocation executeAggregation(String name, Object... args) {
Class>[] argTypes = Arrays.stream(args).map(Object::getClass).toArray(Class[]::new);
@@ -335,6 +343,12 @@ public class StringBasedAggregationUnitTests {
: false;
}
+ @Nullable
+ private ReadPreference readPreferenceOf(AggregationInvocation invocation) {
+ return invocation.aggregation.getOptions() != null ? invocation.aggregation.getOptions().getReadPreference()
+ : null;
+ }
+
private Class> targetTypeOf(AggregationInvocation invocation) {
return invocation.getTargetType();
}
@@ -393,6 +407,9 @@ public class StringBasedAggregationUnitTests {
@Aggregation(pipeline = { RAW_GROUP_BY_LASTNAME_STRING, RAW_OUT })
void outSkipResult();
+
+ @Aggregation(pipeline = { RAW_GROUP_BY_LASTNAME_STRING, RAW_OUT }, readPreference = "secondaryPreferred")
+ void withReadPreference();
}
private interface UnsupportedRepository extends Repository {
diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/support/SimpleMongoRepositoryUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/support/SimpleMongoRepositoryUnitTests.java
index 72ea5366a..a8ac1881a 100644
--- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/support/SimpleMongoRepositoryUnitTests.java
+++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/support/SimpleMongoRepositoryUnitTests.java
@@ -19,20 +19,29 @@ import static org.assertj.core.api.Assertions.*;
import static org.mockito.ArgumentMatchers.*;
import static org.mockito.Mockito.*;
+import java.util.function.Consumer;
+import java.util.stream.Stream;
+
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.Arguments;
+import org.junit.jupiter.params.provider.MethodSource;
import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
-
import org.springframework.data.domain.Example;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Sort;
+import org.springframework.data.mongodb.core.ExecutableFindOperation.ExecutableFind;
import org.springframework.data.mongodb.core.MongoOperations;
import org.springframework.data.mongodb.core.query.Collation;
import org.springframework.data.mongodb.core.query.Query;
+import org.springframework.data.mongodb.repository.ReadPreference;
import org.springframework.data.mongodb.repository.query.MongoEntityInformation;
+import org.springframework.data.repository.core.RepositoryMetadata;
+import org.springframework.data.repository.query.FluentQuery.FetchableFluentQuery;
/**
* @author Christoph Strobl
@@ -133,8 +142,91 @@ public class SimpleMongoRepositoryUnitTests {
assertThat(query.getValue().getCollation()).contains(collation);
}
+ @ParameterizedTest // GH-2971
+ @MethodSource("findAllCalls")
+ void shouldAddReadPreferenceToFindAllMethods(Consumer> findCall) {
+
+ RepositoryMetadata repositoryMetadata = mock(RepositoryMetadata.class);
+ doReturn(TestRepositoryWithReadPreference.class).when(repositoryMetadata).getRepositoryInterface();
+ repository = new SimpleMongoRepository<>(repositoryMetadata, entityInformation, mongoOperations);
+
+ findCall.accept(repository);
+
+ ArgumentCaptor query = ArgumentCaptor.forClass(Query.class);
+ verify(mongoOperations).find(query.capture(), any(), any());
+
+ assertThat(query.getValue().getReadPreference()).isEqualTo(com.mongodb.ReadPreference.secondaryPreferred());
+ }
+
+ @Test // GH-2971
+ void shouldAddReadPreferenceToFindOne() {
+
+ RepositoryMetadata repositoryMetadata = mock(RepositoryMetadata.class);
+ doReturn(TestRepositoryWithReadPreference.class).when(repositoryMetadata).getRepositoryInterface();
+ repository = new SimpleMongoRepository<>(repositoryMetadata, entityInformation, mongoOperations);
+
+ repository.findOne(Example.of(new TestDummy()));
+
+ ArgumentCaptor query = ArgumentCaptor.forClass(Query.class);
+ verify(mongoOperations).findOne(query.capture(), any(), any());
+
+ assertThat(query.getValue().getReadPreference()).isEqualTo(com.mongodb.ReadPreference.secondaryPreferred());
+ }
+
+ @Test // GH-2971
+ void shouldAddReadPreferenceToFluentFetchable() {
+
+ RepositoryMetadata repositoryMetadata = mock(RepositoryMetadata.class);
+ doReturn(TestRepositoryWithReadPreference.class).when(repositoryMetadata).getRepositoryInterface();
+
+ ExecutableFind