Browse Source

DATAMONGO-1894 - Introduce cached ExpressionParser.

Original Pull Request: #874
pull/879/head
Mark Paluch 6 years ago committed by Christoph Strobl
parent
commit
41607b10d0
  1. 5
      spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/AbstractMongoQuery.java
  2. 5
      spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/AbstractReactiveMongoQuery.java
  3. 7
      spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/AggregationUtils.java
  4. 3
      spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/CollationUtils.java
  5. 25
      spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/DefaultSpELExpressionEvaluator.java
  6. 34
      spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/NoOpExpressionEvaluator.java
  7. 4
      spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/PartTreeMongoQuery.java
  8. 3
      spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/QueryUtils.java
  9. 3
      spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/ReactivePartTreeMongoQuery.java
  10. 13
      spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/ReactiveStringBasedAggregation.java
  11. 8
      spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/ReactiveStringBasedMongoQuery.java
  12. 6
      spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/StringBasedAggregation.java
  13. 8
      spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/StringBasedMongoQuery.java
  14. 59
      spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/support/CachingExpressionParser.java
  15. 12
      spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/support/MongoRepositoryFactory.java
  16. 10
      spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/support/ReactiveMongoRepositoryFactory.java
  17. 5
      spring-data-mongodb/src/main/java/org/springframework/data/mongodb/util/json/ParameterBindingContext.java
  18. 1
      src/main/asciidoc/new-features.adoc
  19. 22
      src/main/asciidoc/reference/mongo-repositories.adoc

5
spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/AbstractMongoQuery.java

@ -30,6 +30,7 @@ import org.springframework.data.repository.query.ParameterAccessor;
import org.springframework.data.repository.query.QueryMethodEvaluationContextProvider; import org.springframework.data.repository.query.QueryMethodEvaluationContextProvider;
import org.springframework.data.repository.query.RepositoryQuery; import org.springframework.data.repository.query.RepositoryQuery;
import org.springframework.data.repository.query.ResultProcessor; import org.springframework.data.repository.query.ResultProcessor;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.spel.standard.SpelExpressionParser; import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.lang.Nullable; import org.springframework.lang.Nullable;
import org.springframework.util.Assert; import org.springframework.util.Assert;
@ -47,7 +48,7 @@ public abstract class AbstractMongoQuery implements RepositoryQuery {
private final MongoQueryMethod method; private final MongoQueryMethod method;
private final MongoOperations operations; private final MongoOperations operations;
private final ExecutableFind<?> executableFind; private final ExecutableFind<?> executableFind;
private final SpelExpressionParser expressionParser; private final ExpressionParser expressionParser;
private final QueryMethodEvaluationContextProvider evaluationContextProvider; private final QueryMethodEvaluationContextProvider evaluationContextProvider;
/** /**
@ -58,7 +59,7 @@ public abstract class AbstractMongoQuery implements RepositoryQuery {
* @param expressionParser must not be {@literal null}. * @param expressionParser must not be {@literal null}.
* @param evaluationContextProvider must not be {@literal null}. * @param evaluationContextProvider must not be {@literal null}.
*/ */
public AbstractMongoQuery(MongoQueryMethod method, MongoOperations operations, SpelExpressionParser expressionParser, public AbstractMongoQuery(MongoQueryMethod method, MongoOperations operations, ExpressionParser expressionParser,
QueryMethodEvaluationContextProvider evaluationContextProvider) { QueryMethodEvaluationContextProvider evaluationContextProvider) {
Assert.notNull(operations, "MongoOperations must not be null!"); Assert.notNull(operations, "MongoOperations must not be null!");

5
spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/AbstractReactiveMongoQuery.java

@ -37,6 +37,7 @@ import org.springframework.data.repository.query.ReactiveQueryMethodEvaluationCo
import org.springframework.data.repository.query.RepositoryQuery; import org.springframework.data.repository.query.RepositoryQuery;
import org.springframework.data.repository.query.ResultProcessor; import org.springframework.data.repository.query.ResultProcessor;
import org.springframework.data.util.TypeInformation; import org.springframework.data.util.TypeInformation;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.spel.standard.SpelExpressionParser; import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.lang.Nullable; import org.springframework.lang.Nullable;
import org.springframework.util.Assert; import org.springframework.util.Assert;
@ -54,7 +55,7 @@ public abstract class AbstractReactiveMongoQuery implements RepositoryQuery {
private final ReactiveMongoOperations operations; private final ReactiveMongoOperations operations;
private final EntityInstantiators instantiators; private final EntityInstantiators instantiators;
private final FindWithProjection<?> findOperationWithProjection; private final FindWithProjection<?> findOperationWithProjection;
private final SpelExpressionParser expressionParser; private final ExpressionParser expressionParser;
private final ReactiveQueryMethodEvaluationContextProvider evaluationContextProvider; private final ReactiveQueryMethodEvaluationContextProvider evaluationContextProvider;
/** /**
@ -67,7 +68,7 @@ public abstract class AbstractReactiveMongoQuery implements RepositoryQuery {
* @param evaluationContextProvider must not be {@literal null}. * @param evaluationContextProvider must not be {@literal null}.
*/ */
public AbstractReactiveMongoQuery(ReactiveMongoQueryMethod method, ReactiveMongoOperations operations, public AbstractReactiveMongoQuery(ReactiveMongoQueryMethod method, ReactiveMongoOperations operations,
SpelExpressionParser expressionParser, ReactiveQueryMethodEvaluationContextProvider evaluationContextProvider) { ExpressionParser expressionParser, ReactiveQueryMethodEvaluationContextProvider evaluationContextProvider) {
Assert.notNull(method, "MongoQueryMethod must not be null!"); Assert.notNull(method, "MongoQueryMethod must not be null!");
Assert.notNull(operations, "ReactiveMongoOperations must not be null!"); Assert.notNull(operations, "ReactiveMongoOperations must not be null!");

7
spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/AggregationUtils.java

@ -34,6 +34,7 @@ import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.mongodb.util.json.ParameterBindingContext; import org.springframework.data.mongodb.util.json.ParameterBindingContext;
import org.springframework.data.mongodb.util.json.ParameterBindingDocumentCodec; import org.springframework.data.mongodb.util.json.ParameterBindingDocumentCodec;
import org.springframework.data.repository.query.QueryMethodEvaluationContextProvider; import org.springframework.data.repository.query.QueryMethodEvaluationContextProvider;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.spel.standard.SpelExpressionParser; import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.lang.Nullable; import org.springframework.lang.Nullable;
import org.springframework.util.ClassUtils; import org.springframework.util.ClassUtils;
@ -64,12 +65,12 @@ abstract class AggregationUtils {
* @param accessor must not be {@literal null}. * @param accessor must not be {@literal null}.
* @return the {@link Query} having proper {@link Collation}. * @return the {@link Query} having proper {@link Collation}.
* @see AggregationOptions#getCollation() * @see AggregationOptions#getCollation()
* @see CollationUtils#computeCollation(String, ConvertingParameterAccessor, MongoParameters, SpelExpressionParser, * @see CollationUtils#computeCollation(String, ConvertingParameterAccessor, MongoParameters, ExpressionParser,
* QueryMethodEvaluationContextProvider) * QueryMethodEvaluationContextProvider)
*/ */
static AggregationOptions.Builder applyCollation(AggregationOptions.Builder builder, static AggregationOptions.Builder applyCollation(AggregationOptions.Builder builder,
@Nullable String collationExpression, ConvertingParameterAccessor accessor, MongoParameters parameters, @Nullable String collationExpression, ConvertingParameterAccessor accessor, MongoParameters parameters,
SpelExpressionParser expressionParser, QueryMethodEvaluationContextProvider evaluationContextProvider) { ExpressionParser expressionParser, QueryMethodEvaluationContextProvider evaluationContextProvider) {
Collation collation = CollationUtils.computeCollation(collationExpression, accessor, parameters, expressionParser, Collation collation = CollationUtils.computeCollation(collationExpression, accessor, parameters, expressionParser,
evaluationContextProvider); evaluationContextProvider);
@ -119,7 +120,7 @@ abstract class AggregationUtils {
* @return * @return
*/ */
static List<AggregationOperation> computePipeline(MongoQueryMethod method, ConvertingParameterAccessor accessor, static List<AggregationOperation> computePipeline(MongoQueryMethod method, ConvertingParameterAccessor accessor,
SpelExpressionParser expressionParser, QueryMethodEvaluationContextProvider evaluationContextProvider) { ExpressionParser expressionParser, QueryMethodEvaluationContextProvider evaluationContextProvider) {
ParameterBindingContext bindingContext = new ParameterBindingContext((accessor::getBindableValue), expressionParser, ParameterBindingContext bindingContext = new ParameterBindingContext((accessor::getBindableValue), expressionParser,
() -> evaluationContextProvider.getEvaluationContext(method.getParameters(), accessor.getValues())); () -> evaluationContextProvider.getEvaluationContext(method.getParameters(), accessor.getValues()));

3
spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/CollationUtils.java

@ -24,6 +24,7 @@ import org.springframework.data.mongodb.core.query.Collation;
import org.springframework.data.mongodb.util.json.ParameterBindingContext; import org.springframework.data.mongodb.util.json.ParameterBindingContext;
import org.springframework.data.mongodb.util.json.ParameterBindingDocumentCodec; import org.springframework.data.mongodb.util.json.ParameterBindingDocumentCodec;
import org.springframework.data.repository.query.QueryMethodEvaluationContextProvider; import org.springframework.data.repository.query.QueryMethodEvaluationContextProvider;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.spel.standard.SpelExpressionParser; import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.lang.Nullable; import org.springframework.lang.Nullable;
import org.springframework.util.NumberUtils; import org.springframework.util.NumberUtils;
@ -59,7 +60,7 @@ abstract class CollationUtils {
*/ */
@Nullable @Nullable
static Collation computeCollation(@Nullable String collationExpression, ConvertingParameterAccessor accessor, static Collation computeCollation(@Nullable String collationExpression, ConvertingParameterAccessor accessor,
MongoParameters parameters, SpelExpressionParser expressionParser, MongoParameters parameters, ExpressionParser expressionParser,
QueryMethodEvaluationContextProvider evaluationContextProvider) { QueryMethodEvaluationContextProvider evaluationContextProvider) {
if (accessor.getCollation() != null) { if (accessor.getCollation() != null) {

25
spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/DefaultSpELExpressionEvaluator.java

@ -35,6 +35,15 @@ class DefaultSpELExpressionEvaluator implements SpELExpressionEvaluator {
this.context = context; this.context = context;
} }
/**
* Return a {@link SpELExpressionEvaluator} that does not support expression evaluation.
*
* @return a {@link SpELExpressionEvaluator} that does not support expression evaluation.
*/
public static SpELExpressionEvaluator unsupported() {
return NoOpExpressionEvaluator.INSTANCE;
}
/* /*
* (non-Javadoc) * (non-Javadoc)
* @see org.springframework.data.mapping.model.SpELExpressionEvaluator#evaluate(java.lang.String) * @see org.springframework.data.mapping.model.SpELExpressionEvaluator#evaluate(java.lang.String)
@ -44,4 +53,20 @@ class DefaultSpELExpressionEvaluator implements SpELExpressionEvaluator {
public <T> T evaluate(String expression) { public <T> T evaluate(String expression) {
return (T) parser.parseExpression(expression).getValue(context, Object.class); return (T) parser.parseExpression(expression).getValue(context, Object.class);
} }
/**
* {@link SpELExpressionEvaluator} that does not support SpEL evaluation.
*
* @author Mark Paluch
* @since 3.1
*/
enum NoOpExpressionEvaluator implements SpELExpressionEvaluator {
INSTANCE;
@Override
public <T> T evaluate(String expression) {
throw new UnsupportedOperationException("Expression evaluation not supported");
}
}
} }

34
spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/NoOpExpressionEvaluator.java

@ -1,34 +0,0 @@
/*
* Copyright 2020 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.query;
import org.springframework.data.mapping.model.SpELExpressionEvaluator;
/**
* {@link SpELExpressionEvaluator} that does not support SpEL evaluation.
*
* @author Mark Paluch
* @since 3.1
*/
enum NoOpExpressionEvaluator implements SpELExpressionEvaluator {
INSTANCE;
@Override
public <T> T evaluate(String expression) {
throw new UnsupportedOperationException("Expression evaluation not supported");
}
}

4
spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/PartTreeMongoQuery.java

@ -32,7 +32,7 @@ import org.springframework.data.repository.query.RepositoryQuery;
import org.springframework.data.repository.query.ResultProcessor; import org.springframework.data.repository.query.ResultProcessor;
import org.springframework.data.repository.query.ReturnedType; import org.springframework.data.repository.query.ReturnedType;
import org.springframework.data.repository.query.parser.PartTree; import org.springframework.data.repository.query.parser.PartTree;
import org.springframework.expression.spel.standard.SpelExpressionParser; import org.springframework.expression.ExpressionParser;
import org.springframework.util.StringUtils; import org.springframework.util.StringUtils;
/** /**
@ -59,7 +59,7 @@ public class PartTreeMongoQuery extends AbstractMongoQuery {
* @param evaluationContextProvider must not be {@literal null}. * @param evaluationContextProvider must not be {@literal null}.
*/ */
public PartTreeMongoQuery(MongoQueryMethod method, MongoOperations mongoOperations, public PartTreeMongoQuery(MongoQueryMethod method, MongoOperations mongoOperations,
SpelExpressionParser expressionParser, QueryMethodEvaluationContextProvider evaluationContextProvider) { ExpressionParser expressionParser, QueryMethodEvaluationContextProvider evaluationContextProvider) {
super(method, mongoOperations, expressionParser, evaluationContextProvider); super(method, mongoOperations, expressionParser, evaluationContextProvider);

3
spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/QueryUtils.java

@ -21,6 +21,7 @@ import org.springframework.aop.framework.ProxyFactory;
import org.springframework.data.mongodb.core.query.Collation; import org.springframework.data.mongodb.core.query.Collation;
import org.springframework.data.mongodb.core.query.Query; import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.repository.query.QueryMethodEvaluationContextProvider; import org.springframework.data.repository.query.QueryMethodEvaluationContextProvider;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.spel.standard.SpelExpressionParser; import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.lang.Nullable; import org.springframework.lang.Nullable;
@ -76,7 +77,7 @@ class QueryUtils {
* @since 2.2 * @since 2.2
*/ */
static Query applyCollation(Query query, @Nullable String collationExpression, ConvertingParameterAccessor accessor, static Query applyCollation(Query query, @Nullable String collationExpression, ConvertingParameterAccessor accessor,
MongoParameters parameters, SpelExpressionParser expressionParser, MongoParameters parameters, ExpressionParser expressionParser,
QueryMethodEvaluationContextProvider evaluationContextProvider) { QueryMethodEvaluationContextProvider evaluationContextProvider) {
Collation collation = CollationUtils.computeCollation(collationExpression, accessor, parameters, expressionParser, Collation collation = CollationUtils.computeCollation(collationExpression, accessor, parameters, expressionParser,

3
spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/ReactivePartTreeMongoQuery.java

@ -33,6 +33,7 @@ import org.springframework.data.repository.query.RepositoryQuery;
import org.springframework.data.repository.query.ResultProcessor; import org.springframework.data.repository.query.ResultProcessor;
import org.springframework.data.repository.query.ReturnedType; import org.springframework.data.repository.query.ReturnedType;
import org.springframework.data.repository.query.parser.PartTree; import org.springframework.data.repository.query.parser.PartTree;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.spel.standard.SpelExpressionParser; import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.util.StringUtils; import org.springframework.util.StringUtils;
@ -59,7 +60,7 @@ public class ReactivePartTreeMongoQuery extends AbstractReactiveMongoQuery {
* @param evaluationContextProvider must not be {@literal null}. * @param evaluationContextProvider must not be {@literal null}.
*/ */
public ReactivePartTreeMongoQuery(ReactiveMongoQueryMethod method, ReactiveMongoOperations mongoOperations, public ReactivePartTreeMongoQuery(ReactiveMongoQueryMethod method, ReactiveMongoOperations mongoOperations,
SpelExpressionParser expressionParser, ReactiveQueryMethodEvaluationContextProvider evaluationContextProvider) { ExpressionParser expressionParser, ReactiveQueryMethodEvaluationContextProvider evaluationContextProvider) {
super(method, mongoOperations, expressionParser, evaluationContextProvider); super(method, mongoOperations, expressionParser, evaluationContextProvider);

13
spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/ReactiveStringBasedAggregation.java

@ -39,6 +39,7 @@ import org.springframework.data.mongodb.util.json.ParameterBindingDocumentCodec;
import org.springframework.data.repository.query.ReactiveQueryMethodEvaluationContextProvider; import org.springframework.data.repository.query.ReactiveQueryMethodEvaluationContextProvider;
import org.springframework.data.repository.query.ResultProcessor; import org.springframework.data.repository.query.ResultProcessor;
import org.springframework.data.spel.ExpressionDependencies; import org.springframework.data.spel.ExpressionDependencies;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.spel.standard.SpelExpressionParser; import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.util.ClassUtils; import org.springframework.util.ClassUtils;
@ -54,7 +55,7 @@ public class ReactiveStringBasedAggregation extends AbstractReactiveMongoQuery {
private static final ParameterBindingDocumentCodec CODEC = new ParameterBindingDocumentCodec(); private static final ParameterBindingDocumentCodec CODEC = new ParameterBindingDocumentCodec();
private final SpelExpressionParser expressionParser; private final ExpressionParser expressionParser;
private final ReactiveQueryMethodEvaluationContextProvider evaluationContextProvider; private final ReactiveQueryMethodEvaluationContextProvider evaluationContextProvider;
private final ReactiveMongoOperations reactiveMongoOperations; private final ReactiveMongoOperations reactiveMongoOperations;
private final MongoConverter mongoConverter; private final MongoConverter mongoConverter;
@ -66,7 +67,7 @@ public class ReactiveStringBasedAggregation extends AbstractReactiveMongoQuery {
* @param evaluationContextProvider must not be {@literal null}. * @param evaluationContextProvider must not be {@literal null}.
*/ */
public ReactiveStringBasedAggregation(ReactiveMongoQueryMethod method, public ReactiveStringBasedAggregation(ReactiveMongoQueryMethod method,
ReactiveMongoOperations reactiveMongoOperations, SpelExpressionParser expressionParser, ReactiveMongoOperations reactiveMongoOperations, ExpressionParser expressionParser,
ReactiveQueryMethodEvaluationContextProvider evaluationContextProvider) { ReactiveQueryMethodEvaluationContextProvider evaluationContextProvider) {
super(method, reactiveMongoOperations, expressionParser, evaluationContextProvider); super(method, reactiveMongoOperations, expressionParser, evaluationContextProvider);
@ -118,11 +119,7 @@ public class ReactiveStringBasedAggregation extends AbstractReactiveMongoQuery {
}); });
} }
if (method.isCollectionQuery()) { return method.isCollectionQuery() ? flux : flux.next();
return flux;
} else {
return flux.next();
}
}); });
} }
@ -145,7 +142,7 @@ public class ReactiveStringBasedAggregation extends AbstractReactiveMongoQuery {
it -> evaluationContextProvider.getEvaluationContextLater(method.getParameters(), accessor.getValues(), it)) it -> evaluationContextProvider.getEvaluationContextLater(method.getParameters(), accessor.getValues(), it))
.map(evaluationContext -> evaluationContext .map(evaluationContext -> evaluationContext
.map(it -> (SpELExpressionEvaluator) new DefaultSpELExpressionEvaluator(expressionParser, it))) .map(it -> (SpELExpressionEvaluator) new DefaultSpELExpressionEvaluator(expressionParser, it)))
.orElseGet(() -> Mono.just(NoOpExpressionEvaluator.INSTANCE)); .orElseGet(() -> Mono.just(DefaultSpELExpressionEvaluator.unsupported()));
Mono<AggregationOperation> stage = evaluator.map(it -> { Mono<AggregationOperation> stage = evaluator.map(it -> {

8
spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/ReactiveStringBasedMongoQuery.java

@ -53,7 +53,7 @@ public class ReactiveStringBasedMongoQuery extends AbstractReactiveMongoQuery {
private final String query; private final String query;
private final String fieldSpec; private final String fieldSpec;
private final SpelExpressionParser expressionParser; private final ExpressionParser expressionParser;
private final ReactiveQueryMethodEvaluationContextProvider evaluationContextProvider; private final ReactiveQueryMethodEvaluationContextProvider evaluationContextProvider;
private final boolean isCountQuery; private final boolean isCountQuery;
@ -70,7 +70,7 @@ public class ReactiveStringBasedMongoQuery extends AbstractReactiveMongoQuery {
* @param evaluationContextProvider must not be {@literal null}. * @param evaluationContextProvider must not be {@literal null}.
*/ */
public ReactiveStringBasedMongoQuery(ReactiveMongoQueryMethod method, ReactiveMongoOperations mongoOperations, public ReactiveStringBasedMongoQuery(ReactiveMongoQueryMethod method, ReactiveMongoOperations mongoOperations,
SpelExpressionParser expressionParser, ReactiveQueryMethodEvaluationContextProvider evaluationContextProvider) { ExpressionParser expressionParser, ReactiveQueryMethodEvaluationContextProvider evaluationContextProvider) {
this(method.getAnnotatedQuery(), method, mongoOperations, expressionParser, evaluationContextProvider); this(method.getAnnotatedQuery(), method, mongoOperations, expressionParser, evaluationContextProvider);
} }
@ -85,7 +85,7 @@ public class ReactiveStringBasedMongoQuery extends AbstractReactiveMongoQuery {
* @param expressionParser must not be {@literal null}. * @param expressionParser must not be {@literal null}.
*/ */
public ReactiveStringBasedMongoQuery(String query, ReactiveMongoQueryMethod method, public ReactiveStringBasedMongoQuery(String query, ReactiveMongoQueryMethod method,
ReactiveMongoOperations mongoOperations, SpelExpressionParser expressionParser, ReactiveMongoOperations mongoOperations, ExpressionParser expressionParser,
ReactiveQueryMethodEvaluationContextProvider evaluationContextProvider) { ReactiveQueryMethodEvaluationContextProvider evaluationContextProvider) {
super(method, mongoOperations, expressionParser, evaluationContextProvider); super(method, mongoOperations, expressionParser, evaluationContextProvider);
@ -153,7 +153,7 @@ public class ReactiveStringBasedMongoQuery extends AbstractReactiveMongoQuery {
accessor.getValues(), it)) accessor.getValues(), it))
.map(evaluationContext -> evaluationContext .map(evaluationContext -> evaluationContext
.map(it -> (SpELExpressionEvaluator) new DefaultSpELExpressionEvaluator(expressionParser, it))) .map(it -> (SpELExpressionEvaluator) new DefaultSpELExpressionEvaluator(expressionParser, it)))
.orElseGet(() -> Mono.just(NoOpExpressionEvaluator.INSTANCE)); .orElseGet(() -> Mono.just(DefaultSpELExpressionEvaluator.unsupported()));
return evaluator.map(it -> new ParameterBindingContext(accessor::getBindableValue, it)); return evaluator.map(it -> new ParameterBindingContext(accessor::getBindableValue, it));
} }

6
spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/StringBasedAggregation.java

@ -32,7 +32,7 @@ import org.springframework.data.mongodb.core.mapping.MongoSimpleTypes;
import org.springframework.data.mongodb.core.query.Query; import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.repository.query.QueryMethodEvaluationContextProvider; import org.springframework.data.repository.query.QueryMethodEvaluationContextProvider;
import org.springframework.data.repository.query.ResultProcessor; import org.springframework.data.repository.query.ResultProcessor;
import org.springframework.expression.spel.standard.SpelExpressionParser; import org.springframework.expression.ExpressionParser;
import org.springframework.util.ClassUtils; import org.springframework.util.ClassUtils;
/** /**
@ -43,7 +43,7 @@ public class StringBasedAggregation extends AbstractMongoQuery {
private final MongoOperations mongoOperations; private final MongoOperations mongoOperations;
private final MongoConverter mongoConverter; private final MongoConverter mongoConverter;
private final SpelExpressionParser expressionParser; private final ExpressionParser expressionParser;
private final QueryMethodEvaluationContextProvider evaluationContextProvider; private final QueryMethodEvaluationContextProvider evaluationContextProvider;
/** /**
@ -55,7 +55,7 @@ public class StringBasedAggregation extends AbstractMongoQuery {
* @param evaluationContextProvider * @param evaluationContextProvider
*/ */
public StringBasedAggregation(MongoQueryMethod method, MongoOperations mongoOperations, public StringBasedAggregation(MongoQueryMethod method, MongoOperations mongoOperations,
SpelExpressionParser expressionParser, QueryMethodEvaluationContextProvider evaluationContextProvider) { ExpressionParser expressionParser, QueryMethodEvaluationContextProvider evaluationContextProvider) {
super(method, mongoOperations, expressionParser, evaluationContextProvider); super(method, mongoOperations, expressionParser, evaluationContextProvider);
this.mongoOperations = mongoOperations; this.mongoOperations = mongoOperations;

8
spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/StringBasedMongoQuery.java

@ -54,7 +54,7 @@ public class StringBasedMongoQuery extends AbstractMongoQuery {
private final String fieldSpec; private final String fieldSpec;
private final ParameterBindingDocumentCodec codec; private final ParameterBindingDocumentCodec codec;
private final SpelExpressionParser expressionParser; private final ExpressionParser expressionParser;
private final QueryMethodEvaluationContextProvider evaluationContextProvider; private final QueryMethodEvaluationContextProvider evaluationContextProvider;
private final boolean isCountQuery; private final boolean isCountQuery;
@ -71,7 +71,7 @@ public class StringBasedMongoQuery extends AbstractMongoQuery {
* @param evaluationContextProvider must not be {@literal null}. * @param evaluationContextProvider must not be {@literal null}.
*/ */
public StringBasedMongoQuery(MongoQueryMethod method, MongoOperations mongoOperations, public StringBasedMongoQuery(MongoQueryMethod method, MongoOperations mongoOperations,
SpelExpressionParser expressionParser, QueryMethodEvaluationContextProvider evaluationContextProvider) { ExpressionParser expressionParser, QueryMethodEvaluationContextProvider evaluationContextProvider) {
this(method.getAnnotatedQuery(), method, mongoOperations, expressionParser, evaluationContextProvider); this(method.getAnnotatedQuery(), method, mongoOperations, expressionParser, evaluationContextProvider);
} }
@ -85,7 +85,7 @@ public class StringBasedMongoQuery extends AbstractMongoQuery {
* @param expressionParser must not be {@literal null}. * @param expressionParser must not be {@literal null}.
*/ */
public StringBasedMongoQuery(String query, MongoQueryMethod method, MongoOperations mongoOperations, public StringBasedMongoQuery(String query, MongoQueryMethod method, MongoOperations mongoOperations,
SpelExpressionParser expressionParser, QueryMethodEvaluationContextProvider evaluationContextProvider) { ExpressionParser expressionParser, QueryMethodEvaluationContextProvider evaluationContextProvider) {
super(method, mongoOperations, expressionParser, evaluationContextProvider); super(method, mongoOperations, expressionParser, evaluationContextProvider);
@ -151,7 +151,7 @@ public class StringBasedMongoQuery extends AbstractMongoQuery {
accessor.getValues(), it)) accessor.getValues(), it))
.map(evaluationContext -> (SpELExpressionEvaluator) new DefaultSpELExpressionEvaluator(expressionParser, .map(evaluationContext -> (SpELExpressionEvaluator) new DefaultSpELExpressionEvaluator(expressionParser,
evaluationContext)) evaluationContext))
.orElse(NoOpExpressionEvaluator.INSTANCE); .orElse(DefaultSpELExpressionEvaluator.unsupported());
return new ParameterBindingContext(accessor::getBindableValue, evaluator); return new ParameterBindingContext(accessor::getBindableValue, evaluator);
} }

59
spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/support/CachingExpressionParser.java

@ -0,0 +1,59 @@
/*
* Copyright 2020 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.support;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import org.springframework.expression.Expression;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.ParseException;
import org.springframework.expression.ParserContext;
/**
* Caching variant of {@link ExpressionParser}. This implementation does not support
* {@link #parseExpression(String, ParserContext) parsing with ParseContext}.
*
* @author Mark Paluch
* @since 3.1
*/
class CachingExpressionParser implements ExpressionParser {
private final ExpressionParser delegate;
private final Map<String, Expression> cache = new ConcurrentHashMap<>();
public CachingExpressionParser(ExpressionParser delegate) {
this.delegate = delegate;
}
/*
* (non-Javadoc)
* @see org.springframework.expression.ExpressionParser#parseExpression(java.lang.String)
*/
@Override
public Expression parseExpression(String expressionString) throws ParseException {
return cache.computeIfAbsent(expressionString, delegate::parseExpression);
}
/*
* (non-Javadoc)
* @see org.springframework.expression.ExpressionParser#parseExpression(java.lang.String, org.springframework.expression.ParserContext)
*/
@Override
public Expression parseExpression(String expressionString, ParserContext context) throws ParseException {
throw new UnsupportedOperationException("Parsing using ParserContext is not supported");
}
}

12
spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/support/MongoRepositoryFactory.java

@ -44,6 +44,7 @@ import org.springframework.data.repository.query.QueryLookupStrategy;
import org.springframework.data.repository.query.QueryLookupStrategy.Key; import org.springframework.data.repository.query.QueryLookupStrategy.Key;
import org.springframework.data.repository.query.QueryMethodEvaluationContextProvider; import org.springframework.data.repository.query.QueryMethodEvaluationContextProvider;
import org.springframework.data.repository.query.RepositoryQuery; import org.springframework.data.repository.query.RepositoryQuery;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.spel.standard.SpelExpressionParser; import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.lang.Nullable; import org.springframework.lang.Nullable;
import org.springframework.util.Assert; import org.springframework.util.Assert;
@ -162,7 +163,8 @@ public class MongoRepositoryFactory extends RepositoryFactorySupport {
private final MongoOperations operations; private final MongoOperations operations;
private final QueryMethodEvaluationContextProvider evaluationContextProvider; private final QueryMethodEvaluationContextProvider evaluationContextProvider;
MappingContext<? extends MongoPersistentEntity<?>, MongoPersistentProperty> mappingContext; private final MappingContext<? extends MongoPersistentEntity<?>, MongoPersistentProperty> mappingContext;
private final ExpressionParser expressionParser = new CachingExpressionParser(EXPRESSION_PARSER);
public MongoQueryLookupStrategy(MongoOperations operations, public MongoQueryLookupStrategy(MongoOperations operations,
QueryMethodEvaluationContextProvider evaluationContextProvider, QueryMethodEvaluationContextProvider evaluationContextProvider,
@ -186,14 +188,14 @@ public class MongoRepositoryFactory extends RepositoryFactorySupport {
if (namedQueries.hasQuery(namedQueryName)) { if (namedQueries.hasQuery(namedQueryName)) {
String namedQuery = namedQueries.getQuery(namedQueryName); String namedQuery = namedQueries.getQuery(namedQueryName);
return new StringBasedMongoQuery(namedQuery, queryMethod, operations, EXPRESSION_PARSER, return new StringBasedMongoQuery(namedQuery, queryMethod, operations, expressionParser,
evaluationContextProvider); evaluationContextProvider);
} else if (queryMethod.hasAnnotatedAggregation()) { } else if (queryMethod.hasAnnotatedAggregation()) {
return new StringBasedAggregation(queryMethod, operations, EXPRESSION_PARSER, evaluationContextProvider); return new StringBasedAggregation(queryMethod, operations, expressionParser, evaluationContextProvider);
} else if (queryMethod.hasAnnotatedQuery()) { } else if (queryMethod.hasAnnotatedQuery()) {
return new StringBasedMongoQuery(queryMethod, operations, EXPRESSION_PARSER, evaluationContextProvider); return new StringBasedMongoQuery(queryMethod, operations, expressionParser, evaluationContextProvider);
} else { } else {
return new PartTreeMongoQuery(queryMethod, operations, EXPRESSION_PARSER, evaluationContextProvider); return new PartTreeMongoQuery(queryMethod, operations, expressionParser, evaluationContextProvider);
} }
} }
} }

10
spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/support/ReactiveMongoRepositoryFactory.java

@ -44,6 +44,7 @@ import org.springframework.data.repository.query.QueryLookupStrategy.Key;
import org.springframework.data.repository.query.QueryMethodEvaluationContextProvider; import org.springframework.data.repository.query.QueryMethodEvaluationContextProvider;
import org.springframework.data.repository.query.ReactiveQueryMethodEvaluationContextProvider; import org.springframework.data.repository.query.ReactiveQueryMethodEvaluationContextProvider;
import org.springframework.data.repository.query.RepositoryQuery; import org.springframework.data.repository.query.RepositoryQuery;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.spel.standard.SpelExpressionParser; import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.lang.Nullable; import org.springframework.lang.Nullable;
import org.springframework.util.Assert; import org.springframework.util.Assert;
@ -161,6 +162,7 @@ public class ReactiveMongoRepositoryFactory extends ReactiveRepositoryFactorySup
private final ReactiveMongoOperations operations; private final ReactiveMongoOperations operations;
private final ReactiveQueryMethodEvaluationContextProvider evaluationContextProvider; private final ReactiveQueryMethodEvaluationContextProvider evaluationContextProvider;
private final MappingContext<? extends MongoPersistentEntity<?>, MongoPersistentProperty> mappingContext; private final MappingContext<? extends MongoPersistentEntity<?>, MongoPersistentProperty> mappingContext;
private final ExpressionParser expressionParser = new CachingExpressionParser(EXPRESSION_PARSER);
MongoQueryLookupStrategy(ReactiveMongoOperations operations, MongoQueryLookupStrategy(ReactiveMongoOperations operations,
ReactiveQueryMethodEvaluationContextProvider evaluationContextProvider, ReactiveQueryMethodEvaluationContextProvider evaluationContextProvider,
@ -184,15 +186,15 @@ public class ReactiveMongoRepositoryFactory extends ReactiveRepositoryFactorySup
if (namedQueries.hasQuery(namedQueryName)) { if (namedQueries.hasQuery(namedQueryName)) {
String namedQuery = namedQueries.getQuery(namedQueryName); String namedQuery = namedQueries.getQuery(namedQueryName);
return new ReactiveStringBasedMongoQuery(namedQuery, queryMethod, operations, EXPRESSION_PARSER, return new ReactiveStringBasedMongoQuery(namedQuery, queryMethod, operations, expressionParser,
evaluationContextProvider); evaluationContextProvider);
} else if (queryMethod.hasAnnotatedAggregation()) { } else if (queryMethod.hasAnnotatedAggregation()) {
return new ReactiveStringBasedAggregation(queryMethod, operations, EXPRESSION_PARSER, return new ReactiveStringBasedAggregation(queryMethod, operations, expressionParser,
evaluationContextProvider); evaluationContextProvider);
} else if (queryMethod.hasAnnotatedQuery()) { } else if (queryMethod.hasAnnotatedQuery()) {
return new ReactiveStringBasedMongoQuery(queryMethod, operations, EXPRESSION_PARSER, evaluationContextProvider); return new ReactiveStringBasedMongoQuery(queryMethod, operations, expressionParser, evaluationContextProvider);
} else { } else {
return new ReactivePartTreeMongoQuery(queryMethod, operations, EXPRESSION_PARSER, evaluationContextProvider); return new ReactivePartTreeMongoQuery(queryMethod, operations, expressionParser, evaluationContextProvider);
} }
} }
} }

5
spring-data-mongodb/src/main/java/org/springframework/data/mongodb/util/json/ParameterBindingContext.java

@ -23,6 +23,7 @@ import org.springframework.data.spel.ExpressionDependencies;
import org.springframework.data.util.Lazy; import org.springframework.data.util.Lazy;
import org.springframework.expression.EvaluationContext; import org.springframework.expression.EvaluationContext;
import org.springframework.expression.Expression; import org.springframework.expression.Expression;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.spel.standard.SpelExpressionParser; import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.lang.Nullable; import org.springframework.lang.Nullable;
@ -55,7 +56,7 @@ public class ParameterBindingContext {
* @param evaluationContext a {@link Supplier} for {@link Lazy} context retrieval. * @param evaluationContext a {@link Supplier} for {@link Lazy} context retrieval.
* @since 2.2.3 * @since 2.2.3
*/ */
public ParameterBindingContext(ValueProvider valueProvider, SpelExpressionParser expressionParser, public ParameterBindingContext(ValueProvider valueProvider, ExpressionParser expressionParser,
Supplier<EvaluationContext> evaluationContext) { Supplier<EvaluationContext> evaluationContext) {
this(valueProvider, new SpELExpressionEvaluator() { this(valueProvider, new SpELExpressionEvaluator() {
@ -87,7 +88,7 @@ public class ParameterBindingContext {
* @since 3.1 * @since 3.1
*/ */
public static ParameterBindingContext forExpressions(ValueProvider valueProvider, public static ParameterBindingContext forExpressions(ValueProvider valueProvider,
SpelExpressionParser expressionParser, Function<ExpressionDependencies, EvaluationContext> contextFunction) { ExpressionParser expressionParser, Function<ExpressionDependencies, EvaluationContext> contextFunction) {
return new ParameterBindingContext(valueProvider, new SpELExpressionEvaluator() { return new ParameterBindingContext(valueProvider, new SpELExpressionEvaluator() {
@Override @Override

1
src/main/asciidoc/new-features.adoc

@ -5,6 +5,7 @@
== What's New in Spring Data MongoDB 3.1 == What's New in Spring Data MongoDB 3.1
* <<mongo.auditing,Reactive auditing>> enabled through `@EnableReactiveMongoAuditing`. `@EnableMongoAuditing` no longer registers `ReactiveAuditingEntityCallback`. * <<mongo.auditing,Reactive auditing>> enabled through `@EnableReactiveMongoAuditing`. `@EnableMongoAuditing` no longer registers `ReactiveAuditingEntityCallback`.
* Reactive SpEL support in `@Query` and `@Aggregation` query methods.
[[new-features.3.0]] [[new-features.3.0]]
== What's New in Spring Data MongoDB 3.0 == What's New in Spring Data MongoDB 3.0

22
src/main/asciidoc/reference/mongo-repositories.adoc

@ -131,7 +131,7 @@ The preceding example creates an application context with Spring's unit test sup
[[mongodb.repositories.queries]] [[mongodb.repositories.queries]]
== Query Methods == Query Methods
Most of the data access operations you usually trigger on a repository result in a query being executed against the MongoDB databases. Defining such a query is a matter of declaring a method on the repository interface, as the following example shows: Most of the data access operations you usually trigger on a repository result in a query being executed against the MongoDB databases.Defining such a query is a matter of declaring a method on the repository interface, as the following example shows:
.PersonRepository with query methods .PersonRepository with query methods
==== ====
@ -445,7 +445,7 @@ results in `{ age : -1 }`. Using `Sort.by(ASC, "age")` overrides the defaults an
Query strings and field definitions can be used together with SpEL expressions to create dynamic queries at runtime. Query strings and field definitions can be used together with SpEL expressions to create dynamic queries at runtime.
SpEL expressions can provide predicate values and can be used to extend predicates with subdocuments. SpEL expressions can provide predicate values and can be used to extend predicates with subdocuments.
Expressions expose method arguments through an array that contains all the arguments. The following query uses `[0]` Expressions expose method arguments through an array that contains all the arguments.The following query uses `[0]`
to declare the predicate value for `lastname` (which is equivalent to the `?0` parameter binding): to declare the predicate value for `lastname` (which is equivalent to the `?0` parameter binding):
[source,java] [source,java]
@ -457,7 +457,7 @@ public interface PersonRepository extends MongoRepository<Person, String> {
} }
---- ----
Expressions can be used to invoke functions, evaluate conditionals, and construct values. SpEL expressions Expressions can be used to invoke functions, evaluate conditionals, and construct values.SpEL expressions
used in conjunction with JSON reveal a side-effect, because Map-like declarations inside of SpEL read like JSON, as the following example shows: used in conjunction with JSON reveal a side-effect, because Map-like declarations inside of SpEL read like JSON, as the following example shows:
[source,java] [source,java]
@ -469,12 +469,12 @@ public interface PersonRepository extends MongoRepository<Person, String> {
} }
---- ----
SpEL in query strings can be a powerful way to enhance queries. However, they can also accept a broad range of unwanted arguments. SpEL in query strings can be a powerful way to enhance queries.However, they can also accept a broad range of unwanted arguments.
You should make sure to sanitize strings before passing them to the query to avoid unwanted changes to your query. You should make sure to sanitize strings before passing them to the query to avoid unwanted changes to your query.
Expression support is extensible through the Query SPI: `org.springframework.data.repository.query.spi.EvaluationContextExtension`. Expression support is extensible through the Query SPI: `org.springframework.data.repository.query.spi.EvaluationContextExtension`.
The Query SPI can contribute properties and functions and can customize the root object. Extensions are retrieved from the application context The Query SPI can contribute properties and functions and can customize the root object.Extensions are retrieved from the application context
at the time of SpEL evaluation when the query is built. The following example shows how to use `EvaluationContextExtension`: at the time of SpEL evaluation when the query is built.The following example shows how to use `EvaluationContextExtension`:
[source,java] [source,java]
---- ----
@ -492,13 +492,15 @@ public class SampleEvaluationContextExtension extends EvaluationContextExtension
} }
---- ----
NOTE: Bootstrapping `MongoRepositoryFactory` yourself is not application context-aware and requires further configuration NOTE: Bootstrapping `MongoRepositoryFactory` yourself is not application context-aware and requires further configuration to pick up Query SPI extensions.
to pick up Query SPI extensions.
NOTE: Reactive query methods can make use of `org.springframework.data.spel.spi.ReactiveEvaluationContextExtension`.
[[mongodb.repositories.queries.type-safe]] [[mongodb.repositories.queries.type-safe]]
=== Type-safe Query Methods === Type-safe Query Methods
MongoDB repository support integrates with the http://www.querydsl.com/[Querydsl] project, which provides a way to perform type-safe queries. To quote from the project description, "Instead of writing queries as inline strings or externalizing them into XML files they are constructed via a fluent API." It provides the following features: MongoDB repository support integrates with the http://www.querydsl.com/[Querydsl] project, which provides a way to perform type-safe queries.
To quote from the project description, "Instead of writing queries as inline strings or externalizing them into XML files they are constructed via a fluent API." It provides the following features:
* Code completion in the IDE (all properties, methods, and operations can be expanded in your favorite Java IDE). * Code completion in the IDE (all properties, methods, and operations can be expanded in your favorite Java IDE).
* Almost no syntactically invalid queries allowed (type-safe on all levels). * Almost no syntactically invalid queries allowed (type-safe on all levels).
@ -519,7 +521,7 @@ Page<Person> page = repository.findAll(person.lastname.contains("a"),
PageRequest.of(0, 2, Direction.ASC, "lastname")); PageRequest.of(0, 2, Direction.ASC, "lastname"));
---- ----
`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.Notice that there are no strings in the query other than the `C0123` value.
You can use the generated `Predicate` class by using the `QuerydslPredicateExecutor` interface, which the following listing shows: You can use the generated `Predicate` class by using the `QuerydslPredicateExecutor` interface, which the following listing shows:

Loading…
Cancel
Save