DATAMONGO-1894 - Introduce cached ExpressionParser.
Original Pull Request: #874
This commit is contained in:
committed by
Christoph Strobl
parent
66fae82798
commit
41607b10d0
@@ -30,6 +30,7 @@ import org.springframework.data.repository.query.ParameterAccessor;
|
||||
import org.springframework.data.repository.query.QueryMethodEvaluationContextProvider;
|
||||
import org.springframework.data.repository.query.RepositoryQuery;
|
||||
import org.springframework.data.repository.query.ResultProcessor;
|
||||
import org.springframework.expression.ExpressionParser;
|
||||
import org.springframework.expression.spel.standard.SpelExpressionParser;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.Assert;
|
||||
@@ -47,7 +48,7 @@ public abstract class AbstractMongoQuery implements RepositoryQuery {
|
||||
private final MongoQueryMethod method;
|
||||
private final MongoOperations operations;
|
||||
private final ExecutableFind<?> executableFind;
|
||||
private final SpelExpressionParser expressionParser;
|
||||
private final ExpressionParser expressionParser;
|
||||
private final QueryMethodEvaluationContextProvider evaluationContextProvider;
|
||||
|
||||
/**
|
||||
@@ -58,7 +59,7 @@ public abstract class AbstractMongoQuery implements RepositoryQuery {
|
||||
* @param expressionParser 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) {
|
||||
|
||||
Assert.notNull(operations, "MongoOperations must not be null!");
|
||||
|
||||
@@ -37,6 +37,7 @@ import org.springframework.data.repository.query.ReactiveQueryMethodEvaluationCo
|
||||
import org.springframework.data.repository.query.RepositoryQuery;
|
||||
import org.springframework.data.repository.query.ResultProcessor;
|
||||
import org.springframework.data.util.TypeInformation;
|
||||
import org.springframework.expression.ExpressionParser;
|
||||
import org.springframework.expression.spel.standard.SpelExpressionParser;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.Assert;
|
||||
@@ -54,7 +55,7 @@ public abstract class AbstractReactiveMongoQuery implements RepositoryQuery {
|
||||
private final ReactiveMongoOperations operations;
|
||||
private final EntityInstantiators instantiators;
|
||||
private final FindWithProjection<?> findOperationWithProjection;
|
||||
private final SpelExpressionParser expressionParser;
|
||||
private final ExpressionParser expressionParser;
|
||||
private final ReactiveQueryMethodEvaluationContextProvider evaluationContextProvider;
|
||||
|
||||
/**
|
||||
@@ -67,7 +68,7 @@ public abstract class AbstractReactiveMongoQuery implements RepositoryQuery {
|
||||
* @param evaluationContextProvider must not be {@literal null}.
|
||||
*/
|
||||
public AbstractReactiveMongoQuery(ReactiveMongoQueryMethod method, ReactiveMongoOperations operations,
|
||||
SpelExpressionParser expressionParser, ReactiveQueryMethodEvaluationContextProvider evaluationContextProvider) {
|
||||
ExpressionParser expressionParser, ReactiveQueryMethodEvaluationContextProvider evaluationContextProvider) {
|
||||
|
||||
Assert.notNull(method, "MongoQueryMethod must not be null!");
|
||||
Assert.notNull(operations, "ReactiveMongoOperations must not be null!");
|
||||
|
||||
@@ -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.ParameterBindingDocumentCodec;
|
||||
import org.springframework.data.repository.query.QueryMethodEvaluationContextProvider;
|
||||
import org.springframework.expression.ExpressionParser;
|
||||
import org.springframework.expression.spel.standard.SpelExpressionParser;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.ClassUtils;
|
||||
@@ -64,12 +65,12 @@ abstract class AggregationUtils {
|
||||
* @param accessor must not be {@literal null}.
|
||||
* @return the {@link Query} having proper {@link Collation}.
|
||||
* @see AggregationOptions#getCollation()
|
||||
* @see CollationUtils#computeCollation(String, ConvertingParameterAccessor, MongoParameters, SpelExpressionParser,
|
||||
* @see CollationUtils#computeCollation(String, ConvertingParameterAccessor, MongoParameters, ExpressionParser,
|
||||
* QueryMethodEvaluationContextProvider)
|
||||
*/
|
||||
static AggregationOptions.Builder applyCollation(AggregationOptions.Builder builder,
|
||||
@Nullable String collationExpression, ConvertingParameterAccessor accessor, MongoParameters parameters,
|
||||
SpelExpressionParser expressionParser, QueryMethodEvaluationContextProvider evaluationContextProvider) {
|
||||
ExpressionParser expressionParser, QueryMethodEvaluationContextProvider evaluationContextProvider) {
|
||||
|
||||
Collation collation = CollationUtils.computeCollation(collationExpression, accessor, parameters, expressionParser,
|
||||
evaluationContextProvider);
|
||||
@@ -119,7 +120,7 @@ abstract class AggregationUtils {
|
||||
* @return
|
||||
*/
|
||||
static List<AggregationOperation> computePipeline(MongoQueryMethod method, ConvertingParameterAccessor accessor,
|
||||
SpelExpressionParser expressionParser, QueryMethodEvaluationContextProvider evaluationContextProvider) {
|
||||
ExpressionParser expressionParser, QueryMethodEvaluationContextProvider evaluationContextProvider) {
|
||||
|
||||
ParameterBindingContext bindingContext = new ParameterBindingContext((accessor::getBindableValue), expressionParser,
|
||||
() -> evaluationContextProvider.getEvaluationContext(method.getParameters(), accessor.getValues()));
|
||||
|
||||
@@ -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.ParameterBindingDocumentCodec;
|
||||
import org.springframework.data.repository.query.QueryMethodEvaluationContextProvider;
|
||||
import org.springframework.expression.ExpressionParser;
|
||||
import org.springframework.expression.spel.standard.SpelExpressionParser;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.NumberUtils;
|
||||
@@ -59,7 +60,7 @@ abstract class CollationUtils {
|
||||
*/
|
||||
@Nullable
|
||||
static Collation computeCollation(@Nullable String collationExpression, ConvertingParameterAccessor accessor,
|
||||
MongoParameters parameters, SpelExpressionParser expressionParser,
|
||||
MongoParameters parameters, ExpressionParser expressionParser,
|
||||
QueryMethodEvaluationContextProvider evaluationContextProvider) {
|
||||
|
||||
if (accessor.getCollation() != null) {
|
||||
|
||||
@@ -35,6 +35,15 @@ class DefaultSpELExpressionEvaluator implements SpELExpressionEvaluator {
|
||||
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)
|
||||
* @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) {
|
||||
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");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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");
|
||||
}
|
||||
}
|
||||
@@ -32,7 +32,7 @@ import org.springframework.data.repository.query.RepositoryQuery;
|
||||
import org.springframework.data.repository.query.ResultProcessor;
|
||||
import org.springframework.data.repository.query.ReturnedType;
|
||||
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;
|
||||
|
||||
/**
|
||||
@@ -59,7 +59,7 @@ public class PartTreeMongoQuery extends AbstractMongoQuery {
|
||||
* @param evaluationContextProvider must not be {@literal null}.
|
||||
*/
|
||||
public PartTreeMongoQuery(MongoQueryMethod method, MongoOperations mongoOperations,
|
||||
SpelExpressionParser expressionParser, QueryMethodEvaluationContextProvider evaluationContextProvider) {
|
||||
ExpressionParser expressionParser, QueryMethodEvaluationContextProvider evaluationContextProvider) {
|
||||
|
||||
super(method, mongoOperations, expressionParser, evaluationContextProvider);
|
||||
|
||||
|
||||
@@ -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.Query;
|
||||
import org.springframework.data.repository.query.QueryMethodEvaluationContextProvider;
|
||||
import org.springframework.expression.ExpressionParser;
|
||||
import org.springframework.expression.spel.standard.SpelExpressionParser;
|
||||
import org.springframework.lang.Nullable;
|
||||
|
||||
@@ -76,7 +77,7 @@ class QueryUtils {
|
||||
* @since 2.2
|
||||
*/
|
||||
static Query applyCollation(Query query, @Nullable String collationExpression, ConvertingParameterAccessor accessor,
|
||||
MongoParameters parameters, SpelExpressionParser expressionParser,
|
||||
MongoParameters parameters, ExpressionParser expressionParser,
|
||||
QueryMethodEvaluationContextProvider evaluationContextProvider) {
|
||||
|
||||
Collation collation = CollationUtils.computeCollation(collationExpression, accessor, parameters, expressionParser,
|
||||
|
||||
@@ -33,6 +33,7 @@ import org.springframework.data.repository.query.RepositoryQuery;
|
||||
import org.springframework.data.repository.query.ResultProcessor;
|
||||
import org.springframework.data.repository.query.ReturnedType;
|
||||
import org.springframework.data.repository.query.parser.PartTree;
|
||||
import org.springframework.expression.ExpressionParser;
|
||||
import org.springframework.expression.spel.standard.SpelExpressionParser;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
@@ -59,7 +60,7 @@ public class ReactivePartTreeMongoQuery extends AbstractReactiveMongoQuery {
|
||||
* @param evaluationContextProvider must not be {@literal null}.
|
||||
*/
|
||||
public ReactivePartTreeMongoQuery(ReactiveMongoQueryMethod method, ReactiveMongoOperations mongoOperations,
|
||||
SpelExpressionParser expressionParser, ReactiveQueryMethodEvaluationContextProvider evaluationContextProvider) {
|
||||
ExpressionParser expressionParser, ReactiveQueryMethodEvaluationContextProvider evaluationContextProvider) {
|
||||
|
||||
super(method, mongoOperations, expressionParser, evaluationContextProvider);
|
||||
|
||||
|
||||
@@ -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.ResultProcessor;
|
||||
import org.springframework.data.spel.ExpressionDependencies;
|
||||
import org.springframework.expression.ExpressionParser;
|
||||
import org.springframework.expression.spel.standard.SpelExpressionParser;
|
||||
import org.springframework.util.ClassUtils;
|
||||
|
||||
@@ -54,7 +55,7 @@ public class ReactiveStringBasedAggregation extends AbstractReactiveMongoQuery {
|
||||
|
||||
private static final ParameterBindingDocumentCodec CODEC = new ParameterBindingDocumentCodec();
|
||||
|
||||
private final SpelExpressionParser expressionParser;
|
||||
private final ExpressionParser expressionParser;
|
||||
private final ReactiveQueryMethodEvaluationContextProvider evaluationContextProvider;
|
||||
private final ReactiveMongoOperations reactiveMongoOperations;
|
||||
private final MongoConverter mongoConverter;
|
||||
@@ -66,7 +67,7 @@ public class ReactiveStringBasedAggregation extends AbstractReactiveMongoQuery {
|
||||
* @param evaluationContextProvider must not be {@literal null}.
|
||||
*/
|
||||
public ReactiveStringBasedAggregation(ReactiveMongoQueryMethod method,
|
||||
ReactiveMongoOperations reactiveMongoOperations, SpelExpressionParser expressionParser,
|
||||
ReactiveMongoOperations reactiveMongoOperations, ExpressionParser expressionParser,
|
||||
ReactiveQueryMethodEvaluationContextProvider evaluationContextProvider) {
|
||||
|
||||
super(method, reactiveMongoOperations, expressionParser, evaluationContextProvider);
|
||||
@@ -118,11 +119,7 @@ public class ReactiveStringBasedAggregation extends AbstractReactiveMongoQuery {
|
||||
});
|
||||
}
|
||||
|
||||
if (method.isCollectionQuery()) {
|
||||
return flux;
|
||||
} else {
|
||||
return flux.next();
|
||||
}
|
||||
return method.isCollectionQuery() ? flux : flux.next();
|
||||
});
|
||||
}
|
||||
|
||||
@@ -145,7 +142,7 @@ public class ReactiveStringBasedAggregation extends AbstractReactiveMongoQuery {
|
||||
it -> evaluationContextProvider.getEvaluationContextLater(method.getParameters(), accessor.getValues(), it))
|
||||
.map(evaluationContext -> evaluationContext
|
||||
.map(it -> (SpELExpressionEvaluator) new DefaultSpELExpressionEvaluator(expressionParser, it)))
|
||||
.orElseGet(() -> Mono.just(NoOpExpressionEvaluator.INSTANCE));
|
||||
.orElseGet(() -> Mono.just(DefaultSpELExpressionEvaluator.unsupported()));
|
||||
|
||||
Mono<AggregationOperation> stage = evaluator.map(it -> {
|
||||
|
||||
|
||||
@@ -53,7 +53,7 @@ public class ReactiveStringBasedMongoQuery extends AbstractReactiveMongoQuery {
|
||||
private final String query;
|
||||
private final String fieldSpec;
|
||||
|
||||
private final SpelExpressionParser expressionParser;
|
||||
private final ExpressionParser expressionParser;
|
||||
private final ReactiveQueryMethodEvaluationContextProvider evaluationContextProvider;
|
||||
|
||||
private final boolean isCountQuery;
|
||||
@@ -70,7 +70,7 @@ public class ReactiveStringBasedMongoQuery extends AbstractReactiveMongoQuery {
|
||||
* @param evaluationContextProvider must not be {@literal null}.
|
||||
*/
|
||||
public ReactiveStringBasedMongoQuery(ReactiveMongoQueryMethod method, ReactiveMongoOperations mongoOperations,
|
||||
SpelExpressionParser expressionParser, ReactiveQueryMethodEvaluationContextProvider evaluationContextProvider) {
|
||||
ExpressionParser expressionParser, ReactiveQueryMethodEvaluationContextProvider evaluationContextProvider) {
|
||||
this(method.getAnnotatedQuery(), method, mongoOperations, expressionParser, evaluationContextProvider);
|
||||
}
|
||||
|
||||
@@ -85,7 +85,7 @@ public class ReactiveStringBasedMongoQuery extends AbstractReactiveMongoQuery {
|
||||
* @param expressionParser must not be {@literal null}.
|
||||
*/
|
||||
public ReactiveStringBasedMongoQuery(String query, ReactiveMongoQueryMethod method,
|
||||
ReactiveMongoOperations mongoOperations, SpelExpressionParser expressionParser,
|
||||
ReactiveMongoOperations mongoOperations, ExpressionParser expressionParser,
|
||||
ReactiveQueryMethodEvaluationContextProvider evaluationContextProvider) {
|
||||
|
||||
super(method, mongoOperations, expressionParser, evaluationContextProvider);
|
||||
@@ -153,7 +153,7 @@ public class ReactiveStringBasedMongoQuery extends AbstractReactiveMongoQuery {
|
||||
accessor.getValues(), it))
|
||||
.map(evaluationContext -> evaluationContext
|
||||
.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));
|
||||
}
|
||||
|
||||
@@ -32,7 +32,7 @@ import org.springframework.data.mongodb.core.mapping.MongoSimpleTypes;
|
||||
import org.springframework.data.mongodb.core.query.Query;
|
||||
import org.springframework.data.repository.query.QueryMethodEvaluationContextProvider;
|
||||
import org.springframework.data.repository.query.ResultProcessor;
|
||||
import org.springframework.expression.spel.standard.SpelExpressionParser;
|
||||
import org.springframework.expression.ExpressionParser;
|
||||
import org.springframework.util.ClassUtils;
|
||||
|
||||
/**
|
||||
@@ -43,7 +43,7 @@ public class StringBasedAggregation extends AbstractMongoQuery {
|
||||
|
||||
private final MongoOperations mongoOperations;
|
||||
private final MongoConverter mongoConverter;
|
||||
private final SpelExpressionParser expressionParser;
|
||||
private final ExpressionParser expressionParser;
|
||||
private final QueryMethodEvaluationContextProvider evaluationContextProvider;
|
||||
|
||||
/**
|
||||
@@ -55,7 +55,7 @@ public class StringBasedAggregation extends AbstractMongoQuery {
|
||||
* @param evaluationContextProvider
|
||||
*/
|
||||
public StringBasedAggregation(MongoQueryMethod method, MongoOperations mongoOperations,
|
||||
SpelExpressionParser expressionParser, QueryMethodEvaluationContextProvider evaluationContextProvider) {
|
||||
ExpressionParser expressionParser, QueryMethodEvaluationContextProvider evaluationContextProvider) {
|
||||
super(method, mongoOperations, expressionParser, evaluationContextProvider);
|
||||
|
||||
this.mongoOperations = mongoOperations;
|
||||
|
||||
@@ -54,7 +54,7 @@ public class StringBasedMongoQuery extends AbstractMongoQuery {
|
||||
private final String fieldSpec;
|
||||
|
||||
private final ParameterBindingDocumentCodec codec;
|
||||
private final SpelExpressionParser expressionParser;
|
||||
private final ExpressionParser expressionParser;
|
||||
private final QueryMethodEvaluationContextProvider evaluationContextProvider;
|
||||
|
||||
private final boolean isCountQuery;
|
||||
@@ -71,7 +71,7 @@ public class StringBasedMongoQuery extends AbstractMongoQuery {
|
||||
* @param evaluationContextProvider must not be {@literal null}.
|
||||
*/
|
||||
public StringBasedMongoQuery(MongoQueryMethod method, MongoOperations mongoOperations,
|
||||
SpelExpressionParser expressionParser, QueryMethodEvaluationContextProvider evaluationContextProvider) {
|
||||
ExpressionParser expressionParser, QueryMethodEvaluationContextProvider evaluationContextProvider) {
|
||||
this(method.getAnnotatedQuery(), method, mongoOperations, expressionParser, evaluationContextProvider);
|
||||
}
|
||||
|
||||
@@ -85,7 +85,7 @@ public class StringBasedMongoQuery extends AbstractMongoQuery {
|
||||
* @param expressionParser must not be {@literal null}.
|
||||
*/
|
||||
public StringBasedMongoQuery(String query, MongoQueryMethod method, MongoOperations mongoOperations,
|
||||
SpelExpressionParser expressionParser, QueryMethodEvaluationContextProvider evaluationContextProvider) {
|
||||
ExpressionParser expressionParser, QueryMethodEvaluationContextProvider evaluationContextProvider) {
|
||||
|
||||
super(method, mongoOperations, expressionParser, evaluationContextProvider);
|
||||
|
||||
@@ -151,7 +151,7 @@ public class StringBasedMongoQuery extends AbstractMongoQuery {
|
||||
accessor.getValues(), it))
|
||||
.map(evaluationContext -> (SpELExpressionEvaluator) new DefaultSpELExpressionEvaluator(expressionParser,
|
||||
evaluationContext))
|
||||
.orElse(NoOpExpressionEvaluator.INSTANCE);
|
||||
.orElse(DefaultSpELExpressionEvaluator.unsupported());
|
||||
|
||||
return new ParameterBindingContext(accessor::getBindableValue, evaluator);
|
||||
}
|
||||
|
||||
@@ -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");
|
||||
}
|
||||
}
|
||||
@@ -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.QueryMethodEvaluationContextProvider;
|
||||
import org.springframework.data.repository.query.RepositoryQuery;
|
||||
import org.springframework.expression.ExpressionParser;
|
||||
import org.springframework.expression.spel.standard.SpelExpressionParser;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.Assert;
|
||||
@@ -162,7 +163,8 @@ public class MongoRepositoryFactory extends RepositoryFactorySupport {
|
||||
|
||||
private final MongoOperations operations;
|
||||
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,
|
||||
QueryMethodEvaluationContextProvider evaluationContextProvider,
|
||||
@@ -186,14 +188,14 @@ public class MongoRepositoryFactory extends RepositoryFactorySupport {
|
||||
|
||||
if (namedQueries.hasQuery(namedQueryName)) {
|
||||
String namedQuery = namedQueries.getQuery(namedQueryName);
|
||||
return new StringBasedMongoQuery(namedQuery, queryMethod, operations, EXPRESSION_PARSER,
|
||||
return new StringBasedMongoQuery(namedQuery, queryMethod, operations, expressionParser,
|
||||
evaluationContextProvider);
|
||||
} else if (queryMethod.hasAnnotatedAggregation()) {
|
||||
return new StringBasedAggregation(queryMethod, operations, EXPRESSION_PARSER, evaluationContextProvider);
|
||||
return new StringBasedAggregation(queryMethod, operations, expressionParser, evaluationContextProvider);
|
||||
} else if (queryMethod.hasAnnotatedQuery()) {
|
||||
return new StringBasedMongoQuery(queryMethod, operations, EXPRESSION_PARSER, evaluationContextProvider);
|
||||
return new StringBasedMongoQuery(queryMethod, operations, expressionParser, evaluationContextProvider);
|
||||
} else {
|
||||
return new PartTreeMongoQuery(queryMethod, operations, EXPRESSION_PARSER, evaluationContextProvider);
|
||||
return new PartTreeMongoQuery(queryMethod, operations, expressionParser, evaluationContextProvider);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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.ReactiveQueryMethodEvaluationContextProvider;
|
||||
import org.springframework.data.repository.query.RepositoryQuery;
|
||||
import org.springframework.expression.ExpressionParser;
|
||||
import org.springframework.expression.spel.standard.SpelExpressionParser;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.Assert;
|
||||
@@ -161,6 +162,7 @@ public class ReactiveMongoRepositoryFactory extends ReactiveRepositoryFactorySup
|
||||
private final ReactiveMongoOperations operations;
|
||||
private final ReactiveQueryMethodEvaluationContextProvider evaluationContextProvider;
|
||||
private final MappingContext<? extends MongoPersistentEntity<?>, MongoPersistentProperty> mappingContext;
|
||||
private final ExpressionParser expressionParser = new CachingExpressionParser(EXPRESSION_PARSER);
|
||||
|
||||
MongoQueryLookupStrategy(ReactiveMongoOperations operations,
|
||||
ReactiveQueryMethodEvaluationContextProvider evaluationContextProvider,
|
||||
@@ -184,15 +186,15 @@ public class ReactiveMongoRepositoryFactory extends ReactiveRepositoryFactorySup
|
||||
|
||||
if (namedQueries.hasQuery(namedQueryName)) {
|
||||
String namedQuery = namedQueries.getQuery(namedQueryName);
|
||||
return new ReactiveStringBasedMongoQuery(namedQuery, queryMethod, operations, EXPRESSION_PARSER,
|
||||
return new ReactiveStringBasedMongoQuery(namedQuery, queryMethod, operations, expressionParser,
|
||||
evaluationContextProvider);
|
||||
} else if (queryMethod.hasAnnotatedAggregation()) {
|
||||
return new ReactiveStringBasedAggregation(queryMethod, operations, EXPRESSION_PARSER,
|
||||
return new ReactiveStringBasedAggregation(queryMethod, operations, expressionParser,
|
||||
evaluationContextProvider);
|
||||
} else if (queryMethod.hasAnnotatedQuery()) {
|
||||
return new ReactiveStringBasedMongoQuery(queryMethod, operations, EXPRESSION_PARSER, evaluationContextProvider);
|
||||
return new ReactiveStringBasedMongoQuery(queryMethod, operations, expressionParser, evaluationContextProvider);
|
||||
} else {
|
||||
return new ReactivePartTreeMongoQuery(queryMethod, operations, EXPRESSION_PARSER, evaluationContextProvider);
|
||||
return new ReactivePartTreeMongoQuery(queryMethod, operations, expressionParser, evaluationContextProvider);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,6 +23,7 @@ import org.springframework.data.spel.ExpressionDependencies;
|
||||
import org.springframework.data.util.Lazy;
|
||||
import org.springframework.expression.EvaluationContext;
|
||||
import org.springframework.expression.Expression;
|
||||
import org.springframework.expression.ExpressionParser;
|
||||
import org.springframework.expression.spel.standard.SpelExpressionParser;
|
||||
import org.springframework.lang.Nullable;
|
||||
|
||||
@@ -55,7 +56,7 @@ public class ParameterBindingContext {
|
||||
* @param evaluationContext a {@link Supplier} for {@link Lazy} context retrieval.
|
||||
* @since 2.2.3
|
||||
*/
|
||||
public ParameterBindingContext(ValueProvider valueProvider, SpelExpressionParser expressionParser,
|
||||
public ParameterBindingContext(ValueProvider valueProvider, ExpressionParser expressionParser,
|
||||
Supplier<EvaluationContext> evaluationContext) {
|
||||
|
||||
this(valueProvider, new SpELExpressionEvaluator() {
|
||||
@@ -87,7 +88,7 @@ public class ParameterBindingContext {
|
||||
* @since 3.1
|
||||
*/
|
||||
public static ParameterBindingContext forExpressions(ValueProvider valueProvider,
|
||||
SpelExpressionParser expressionParser, Function<ExpressionDependencies, EvaluationContext> contextFunction) {
|
||||
ExpressionParser expressionParser, Function<ExpressionDependencies, EvaluationContext> contextFunction) {
|
||||
|
||||
return new ParameterBindingContext(valueProvider, new SpELExpressionEvaluator() {
|
||||
@Override
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
== What's New in Spring Data MongoDB 3.1
|
||||
|
||||
* <<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]]
|
||||
== What's New in Spring Data MongoDB 3.0
|
||||
|
||||
@@ -131,7 +131,7 @@ The preceding example creates an application context with Spring's unit test sup
|
||||
[[mongodb.repositories.queries]]
|
||||
== 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
|
||||
====
|
||||
@@ -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.
|
||||
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):
|
||||
|
||||
[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:
|
||||
|
||||
[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.
|
||||
|
||||
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
|
||||
at the time of SpEL evaluation when the query is built. The following example shows how to use `EvaluationContextExtension`:
|
||||
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`:
|
||||
|
||||
[source,java]
|
||||
----
|
||||
@@ -492,13 +492,15 @@ public class SampleEvaluationContextExtension extends EvaluationContextExtension
|
||||
}
|
||||
----
|
||||
|
||||
NOTE: Bootstrapping `MongoRepositoryFactory` yourself is not application context-aware and requires further configuration
|
||||
to pick up Query SPI extensions.
|
||||
NOTE: Bootstrapping `MongoRepositoryFactory` yourself is not application context-aware and requires further configuration 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]]
|
||||
=== 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).
|
||||
* 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"));
|
||||
----
|
||||
|
||||
`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:
|
||||
|
||||
|
||||
Reference in New Issue
Block a user