Added support for Repository Query Method ValueExpressions.
Closes #1522 Original pull request: #1523
This commit is contained in:
committed by
Mark Paluch
parent
77453ae9a0
commit
9ebd3db34f
@@ -19,7 +19,7 @@ import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.data.mapping.model.SpELExpressionEvaluator;
|
||||
import org.springframework.data.mapping.model.ValueExpressionEvaluator;
|
||||
import org.springframework.data.repository.query.Parameter;
|
||||
import org.springframework.data.repository.query.ParameterAccessor;
|
||||
import org.springframework.lang.Nullable;
|
||||
@@ -39,13 +39,13 @@ class BindingContext {
|
||||
|
||||
private final List<ParameterBinding> bindings;
|
||||
|
||||
private final SpELExpressionEvaluator evaluator;
|
||||
private final ValueExpressionEvaluator evaluator;
|
||||
|
||||
/**
|
||||
* Create new {@link BindingContext}.
|
||||
*/
|
||||
public BindingContext(CassandraParameters parameters, ParameterAccessor parameterAccessor,
|
||||
List<ParameterBinding> bindings, SpELExpressionEvaluator evaluator) {
|
||||
BindingContext(CassandraParameters parameters, ParameterAccessor parameterAccessor,
|
||||
List<ParameterBinding> bindings, ValueExpressionEvaluator evaluator) {
|
||||
|
||||
this.parameters = parameters;
|
||||
this.parameterAccessor = parameterAccessor;
|
||||
|
||||
@@ -32,7 +32,7 @@ import org.springframework.data.cassandra.core.query.Query;
|
||||
import org.springframework.data.cassandra.repository.Query.Idempotency;
|
||||
import org.springframework.data.domain.Limit;
|
||||
import org.springframework.data.mapping.context.MappingContext;
|
||||
import org.springframework.data.mapping.model.SpELExpressionEvaluator;
|
||||
import org.springframework.data.mapping.model.ValueExpressionEvaluator;
|
||||
import org.springframework.data.repository.query.QueryCreationException;
|
||||
import org.springframework.data.repository.query.ResultProcessor;
|
||||
import org.springframework.data.repository.query.ReturnedType;
|
||||
@@ -249,7 +249,7 @@ class QueryStatementCreator {
|
||||
* @return the {@link Statement}.
|
||||
*/
|
||||
SimpleStatement select(StringBasedQuery stringBasedQuery, CassandraParameterAccessor parameterAccessor,
|
||||
SpELExpressionEvaluator evaluator) {
|
||||
ValueExpressionEvaluator evaluator) {
|
||||
|
||||
try {
|
||||
|
||||
|
||||
@@ -17,11 +17,17 @@ package org.springframework.data.cassandra.repository.query;
|
||||
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import org.springframework.core.env.StandardEnvironment;
|
||||
import org.springframework.data.cassandra.core.ReactiveCassandraOperations;
|
||||
import org.springframework.data.cassandra.repository.Query;
|
||||
import org.springframework.data.mapping.model.SpELExpressionEvaluator;
|
||||
import org.springframework.data.expression.ReactiveValueEvaluationContextProvider;
|
||||
import org.springframework.data.expression.ValueEvaluationContextProvider;
|
||||
import org.springframework.data.expression.ValueExpressionParser;
|
||||
import org.springframework.data.mapping.model.ValueExpressionEvaluator;
|
||||
import org.springframework.data.repository.query.QueryMethodEvaluationContextProvider;
|
||||
import org.springframework.data.repository.query.QueryMethodValueEvaluationContextAccessor;
|
||||
import org.springframework.data.repository.query.ReactiveQueryMethodEvaluationContextProvider;
|
||||
import org.springframework.data.repository.query.ValueExpressionDelegate;
|
||||
import org.springframework.data.spel.ExpressionDependencies;
|
||||
import org.springframework.expression.ExpressionParser;
|
||||
import org.springframework.expression.spel.standard.SpelExpressionParser;
|
||||
@@ -51,8 +57,9 @@ public class ReactiveStringBasedCassandraQuery extends AbstractReactiveCassandra
|
||||
|
||||
private final boolean isExistsQuery;
|
||||
|
||||
private final ExpressionParser expressionParser;
|
||||
private final ReactiveQueryMethodEvaluationContextProvider evaluationContextProvider;
|
||||
private final ValueExpressionDelegate delegate;
|
||||
|
||||
private final ReactiveValueEvaluationContextProvider valueEvaluationContextProvider;
|
||||
|
||||
/**
|
||||
* Create a new {@link ReactiveStringBasedCassandraQuery} for the given {@link CassandraQueryMethod},
|
||||
@@ -66,7 +73,9 @@ public class ReactiveStringBasedCassandraQuery extends AbstractReactiveCassandra
|
||||
* {@link org.springframework.expression.spel.support.StandardEvaluationContext}.
|
||||
* @see org.springframework.data.cassandra.repository.query.ReactiveCassandraQueryMethod
|
||||
* @see org.springframework.data.cassandra.core.ReactiveCassandraOperations
|
||||
* @deprecated since 4.4, use the constructors accepting {@link ValueExpressionDelegate} instead.
|
||||
*/
|
||||
@Deprecated(since = "4.4")
|
||||
public ReactiveStringBasedCassandraQuery(ReactiveCassandraQueryMethod queryMethod,
|
||||
ReactiveCassandraOperations operations, ExpressionParser expressionParser,
|
||||
ReactiveQueryMethodEvaluationContextProvider evaluationContextProvider) {
|
||||
@@ -86,19 +95,59 @@ public class ReactiveStringBasedCassandraQuery extends AbstractReactiveCassandra
|
||||
* {@link org.springframework.expression.spel.support.StandardEvaluationContext}.
|
||||
* @see org.springframework.data.cassandra.repository.query.ReactiveCassandraQueryMethod
|
||||
* @see org.springframework.data.cassandra.core.ReactiveCassandraOperations
|
||||
* @deprecated since 4.4, use the constructors accepting {@link ValueExpressionDelegate} instead.
|
||||
*/
|
||||
@Deprecated(since = "4.4")
|
||||
public ReactiveStringBasedCassandraQuery(String query, ReactiveCassandraQueryMethod method,
|
||||
ReactiveCassandraOperations operations, ExpressionParser expressionParser,
|
||||
ReactiveQueryMethodEvaluationContextProvider evaluationContextProvider) {
|
||||
|
||||
this(query, method, operations, new ValueExpressionDelegate(new QueryMethodValueEvaluationContextAccessor(new StandardEnvironment(), evaluationContextProvider.getEvaluationContextProvider()), ValueExpressionParser.create(() -> expressionParser)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new {@link ReactiveStringBasedCassandraQuery} for the given {@link CassandraQueryMethod},
|
||||
* {@link ReactiveCassandraOperations}, {@link ValueExpressionDelegate}
|
||||
*
|
||||
* @param queryMethod {@link ReactiveCassandraQueryMethod} on which this query is based.
|
||||
* @param operations {@link ReactiveCassandraOperations} used to perform data access in Cassandra.
|
||||
* @param delegate {@link ValueExpressionDelegate} used to parse expressions in the query.
|
||||
* @see org.springframework.data.cassandra.repository.query.ReactiveCassandraQueryMethod
|
||||
* @see org.springframework.data.cassandra.core.ReactiveCassandraOperations
|
||||
* @since 4.4
|
||||
*/
|
||||
public ReactiveStringBasedCassandraQuery(ReactiveCassandraQueryMethod queryMethod,
|
||||
ReactiveCassandraOperations operations, ValueExpressionDelegate delegate) {
|
||||
|
||||
this(queryMethod.getRequiredAnnotatedQuery(), queryMethod, operations, delegate);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new {@link ReactiveStringBasedCassandraQuery} for the given {@code query}, {@link CassandraQueryMethod},
|
||||
* {@link ReactiveCassandraOperations}, {@link ValueExpressionDelegate}
|
||||
*
|
||||
* @param method {@link ReactiveCassandraQueryMethod} on which this query is based.
|
||||
* @param operations {@link ReactiveCassandraOperations} used to perform data access in Cassandra.
|
||||
* @param delegate {@link SpelExpressionParser} used to parse expressions in the query.
|
||||
* @see org.springframework.data.cassandra.repository.query.ReactiveCassandraQueryMethod
|
||||
* @see org.springframework.data.cassandra.core.ReactiveCassandraOperations
|
||||
* @since 4.4
|
||||
*/
|
||||
public ReactiveStringBasedCassandraQuery(String query, ReactiveCassandraQueryMethod method,
|
||||
ReactiveCassandraOperations operations, ValueExpressionDelegate delegate) {
|
||||
|
||||
super(method, operations);
|
||||
|
||||
Assert.hasText(query, "Query must not be empty");
|
||||
|
||||
this.expressionParser = expressionParser;
|
||||
this.evaluationContextProvider = evaluationContextProvider;
|
||||
this.delegate = delegate;
|
||||
|
||||
this.stringBasedQuery = new StringBasedQuery(query, method.getParameters(), expressionParser);
|
||||
this.stringBasedQuery = new StringBasedQuery(query, method.getParameters(), delegate);
|
||||
|
||||
ValueEvaluationContextProvider valueContextProvider = delegate.createValueContextProvider(
|
||||
method.getParameters());
|
||||
Assert.isInstanceOf(ReactiveValueEvaluationContextProvider.class, valueContextProvider, "ValueEvaluationContextProvider must be reactive");
|
||||
this.valueEvaluationContextProvider = (ReactiveValueEvaluationContextProvider) valueContextProvider;
|
||||
|
||||
if (method.hasAnnotatedQuery()) {
|
||||
|
||||
@@ -126,10 +175,9 @@ public class ReactiveStringBasedCassandraQuery extends AbstractReactiveCassandra
|
||||
StringBasedQuery query = getStringBasedQuery();
|
||||
ConvertingParameterAccessor parameterAccessorToUse = new ConvertingParameterAccessor(
|
||||
getReactiveCassandraOperations().getConverter(), parameterAccessor);
|
||||
Mono<SpELExpressionEvaluator> spelEvaluator = getSpelEvaluatorFor(query.getExpressionDependencies(),
|
||||
parameterAccessorToUse);
|
||||
|
||||
return spelEvaluator.map(it -> getQueryStatementCreator().select(query, parameterAccessorToUse, it));
|
||||
return getValueExpressionEvaluatorLater(query.getExpressionDependencies(), parameterAccessor)
|
||||
.map(it -> getQueryStatementCreator().select(query, parameterAccessorToUse, it));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -152,21 +200,9 @@ public class ReactiveStringBasedCassandraQuery extends AbstractReactiveCassandra
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtain a {@link Mono publisher} emitting the {@link SpELExpressionEvaluator} suitable to evaluate expressions
|
||||
* backed by the given dependencies.
|
||||
*
|
||||
* @param dependencies must not be {@literal null}.
|
||||
* @param accessor must not be {@literal null}.
|
||||
* @return a {@link Mono} emitting the {@link SpELExpressionEvaluator} when ready.
|
||||
*/
|
||||
private Mono<SpELExpressionEvaluator> getSpelEvaluatorFor(ExpressionDependencies dependencies,
|
||||
private Mono<ValueExpressionEvaluator> getValueExpressionEvaluatorLater(ExpressionDependencies dependencies,
|
||||
CassandraParameterAccessor accessor) {
|
||||
|
||||
return evaluationContextProvider
|
||||
.getEvaluationContextLater(getQueryMethod().getParameters(), accessor.getValues(), dependencies)
|
||||
.map(evaluationContext -> (SpELExpressionEvaluator) new DefaultSpELExpressionEvaluator(expressionParser,
|
||||
evaluationContext))
|
||||
.defaultIfEmpty(DefaultSpELExpressionEvaluator.unsupported());
|
||||
return valueEvaluationContextProvider.getEvaluationContextLater(accessor.getValues(), dependencies)
|
||||
.map(evaluationContext -> new ValueExpressionDelegateValueExpressionEvaluator(delegate, valueExpression -> evaluationContext));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,10 +15,14 @@
|
||||
*/
|
||||
package org.springframework.data.cassandra.repository.query;
|
||||
|
||||
import org.springframework.core.env.StandardEnvironment;
|
||||
import org.springframework.data.cassandra.core.CassandraOperations;
|
||||
import org.springframework.data.cassandra.repository.Query;
|
||||
import org.springframework.data.expression.ValueEvaluationContext;
|
||||
import org.springframework.data.expression.ValueExpressionParser;
|
||||
import org.springframework.data.repository.query.QueryMethodEvaluationContextProvider;
|
||||
import org.springframework.expression.EvaluationContext;
|
||||
import org.springframework.data.repository.query.QueryMethodValueEvaluationContextAccessor;
|
||||
import org.springframework.data.repository.query.ValueExpressionDelegate;
|
||||
import org.springframework.expression.ExpressionParser;
|
||||
import org.springframework.expression.spel.standard.SpelExpressionParser;
|
||||
|
||||
@@ -46,8 +50,7 @@ public class StringBasedCassandraQuery extends AbstractCassandraQuery {
|
||||
|
||||
private final boolean isExistsQuery;
|
||||
|
||||
private final ExpressionParser expressionParser;
|
||||
private final QueryMethodEvaluationContextProvider evaluationContextProvider;
|
||||
private final ValueExpressionDelegate valueExpressionDelegate;
|
||||
|
||||
/**
|
||||
* Create a new {@link StringBasedCassandraQuery} for the given {@link CassandraQueryMethod},
|
||||
@@ -60,35 +63,52 @@ public class StringBasedCassandraQuery extends AbstractCassandraQuery {
|
||||
* {@link org.springframework.expression.spel.support.StandardEvaluationContext}.
|
||||
* @see org.springframework.data.cassandra.repository.query.CassandraQueryMethod
|
||||
* @see org.springframework.data.cassandra.core.CassandraOperations
|
||||
* @deprecated use the constructor version with {@link ValueExpressionDelegate}
|
||||
*/
|
||||
@Deprecated(since = "4.4")
|
||||
public StringBasedCassandraQuery(CassandraQueryMethod queryMethod, CassandraOperations operations,
|
||||
ExpressionParser expressionParser, QueryMethodEvaluationContextProvider evaluationContextProvider) {
|
||||
|
||||
this(queryMethod.getRequiredAnnotatedQuery(), queryMethod, operations, expressionParser, evaluationContextProvider);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new {@link StringBasedCassandraQuery} for the given {@link CassandraQueryMethod},
|
||||
* {@link CassandraOperations}, {@link ValueExpressionDelegate}.
|
||||
*
|
||||
* @param queryMethod {@link CassandraQueryMethod} on which this query is based.
|
||||
* @param operations {@link CassandraOperations} used to perform data access in Cassandra.
|
||||
* @param valueExpressionDelegate {@link ValueExpressionDelegate} used to parse expressions in the query.
|
||||
* @see org.springframework.data.cassandra.repository.query.CassandraQueryMethod
|
||||
* @see org.springframework.data.cassandra.core.CassandraOperations
|
||||
* @since 4.4
|
||||
*/
|
||||
public StringBasedCassandraQuery(CassandraQueryMethod queryMethod, CassandraOperations operations,
|
||||
ValueExpressionDelegate valueExpressionDelegate) {
|
||||
|
||||
this(queryMethod.getRequiredAnnotatedQuery(), queryMethod, operations, valueExpressionDelegate);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new {@link StringBasedCassandraQuery} for the given {@code query}, {@link CassandraQueryMethod},
|
||||
* {@link CassandraOperations}, {@link SpelExpressionParser}, and {@link QueryMethodEvaluationContextProvider}.
|
||||
* {@link CassandraOperations}, {@link ValueExpressionDelegate}.
|
||||
*
|
||||
* @param query {@link String} containing the Apache Cassandra CQL query to execute.
|
||||
* @param method {@link CassandraQueryMethod} on which this query is based.
|
||||
* @param operations {@link CassandraOperations} used to perform data access in Cassandra.
|
||||
* @param expressionParser {@link SpelExpressionParser} used to parse expressions in the query.
|
||||
* @param evaluationContextProvider {@link QueryMethodEvaluationContextProvider} used to access the potentially shared
|
||||
* {@link org.springframework.expression.spel.support.StandardEvaluationContext}.
|
||||
* @param valueExpressionDelegate {@link ValueExpressionDelegate} used to parse expressions in the query.
|
||||
* @see org.springframework.data.cassandra.repository.query.CassandraQueryMethod
|
||||
* @see org.springframework.data.cassandra.core.CassandraOperations
|
||||
* @since 4.4
|
||||
*/
|
||||
public StringBasedCassandraQuery(String query, CassandraQueryMethod method, CassandraOperations operations,
|
||||
ExpressionParser expressionParser, QueryMethodEvaluationContextProvider evaluationContextProvider) {
|
||||
ValueExpressionDelegate valueExpressionDelegate) {
|
||||
|
||||
super(method, operations);
|
||||
|
||||
this.expressionParser = expressionParser;
|
||||
this.evaluationContextProvider = evaluationContextProvider;
|
||||
this.valueExpressionDelegate = valueExpressionDelegate;
|
||||
|
||||
this.stringBasedQuery = new StringBasedQuery(query, method.getParameters(), expressionParser);
|
||||
this.stringBasedQuery = new StringBasedQuery(query, method.getParameters(), valueExpressionDelegate);
|
||||
|
||||
if (method.hasAnnotatedQuery()) {
|
||||
|
||||
@@ -106,6 +126,26 @@ public class StringBasedCassandraQuery extends AbstractCassandraQuery {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new {@link StringBasedCassandraQuery} for the given {@code query}, {@link CassandraQueryMethod},
|
||||
* {@link CassandraOperations}, {@link SpelExpressionParser}, and {@link QueryMethodEvaluationContextProvider}.
|
||||
*
|
||||
* @param query {@link String} containing the Apache Cassandra CQL query to execute.
|
||||
* @param method {@link CassandraQueryMethod} on which this query is based.
|
||||
* @param operations {@link CassandraOperations} used to perform data access in Cassandra.
|
||||
* @param expressionParser {@link SpelExpressionParser} used to parse expressions in the query.
|
||||
* @param evaluationContextProvider {@link QueryMethodEvaluationContextProvider} used to access the potentially shared
|
||||
* {@link org.springframework.expression.spel.support.StandardEvaluationContext}.
|
||||
* @see org.springframework.data.cassandra.repository.query.CassandraQueryMethod
|
||||
* @see org.springframework.data.cassandra.core.CassandraOperations
|
||||
* @deprecated use the constructor version with {@link ValueExpressionDelegate}
|
||||
*/
|
||||
@Deprecated(since = "4.4")
|
||||
public StringBasedCassandraQuery(String query, CassandraQueryMethod method, CassandraOperations operations,
|
||||
ExpressionParser expressionParser, QueryMethodEvaluationContextProvider evaluationContextProvider) {
|
||||
this(query, method, operations, new ValueExpressionDelegate(new QueryMethodValueEvaluationContextAccessor(new StandardEnvironment(), evaluationContextProvider.getEvaluationContextProvider()), ValueExpressionParser.create(() -> expressionParser)));
|
||||
}
|
||||
|
||||
protected StringBasedQuery getStringBasedQuery() {
|
||||
return this.stringBasedQuery;
|
||||
}
|
||||
@@ -116,11 +156,10 @@ public class StringBasedCassandraQuery extends AbstractCassandraQuery {
|
||||
StringBasedQuery query = getStringBasedQuery();
|
||||
ConvertingParameterAccessor parameterAccessorToUse = new ConvertingParameterAccessor(getOperations().getConverter(),
|
||||
parameterAccessor);
|
||||
EvaluationContext evaluationContext = evaluationContextProvider.getEvaluationContext(
|
||||
getQueryMethod().getParameters(), parameterAccessorToUse.getValues(), query.getExpressionDependencies());
|
||||
ValueEvaluationContext evaluationContext = valueExpressionDelegate.createValueContextProvider(
|
||||
getQueryMethod().getParameters()).getEvaluationContext(parameterAccessorToUse.getValues(), query.getExpressionDependencies());
|
||||
|
||||
return getQueryStatementCreator().select(query, parameterAccessorToUse,
|
||||
new DefaultSpELExpressionEvaluator(expressionParser, evaluationContext));
|
||||
return getQueryStatementCreator().select(query, parameterAccessorToUse, new ValueExpressionDelegateValueExpressionEvaluator(valueExpressionDelegate, valueExpression -> evaluationContext));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -17,14 +17,15 @@ package org.springframework.data.cassandra.repository.query;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.TreeMap;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import org.springframework.data.cassandra.repository.query.BindingContext.ParameterBinding;
|
||||
import org.springframework.data.mapping.model.SpELExpressionEvaluator;
|
||||
import org.springframework.data.mapping.model.ValueExpressionEvaluator;
|
||||
import org.springframework.data.repository.query.ValueExpressionDelegate;
|
||||
import org.springframework.data.spel.ExpressionDependencies;
|
||||
import org.springframework.expression.ExpressionParser;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.StringUtils;
|
||||
@@ -43,21 +44,20 @@ class StringBasedQuery {
|
||||
|
||||
private final CassandraParameters parameters;
|
||||
|
||||
private final ExpressionParser expressionParser;
|
||||
private final ValueExpressionDelegate expressionParser;
|
||||
|
||||
private final List<ParameterBinding> queryParameterBindings = new ArrayList<>();
|
||||
|
||||
private final ExpressionDependencies expressionDependencies;
|
||||
|
||||
/**
|
||||
* Create a new {@link StringBasedQuery} given {@code query}, {@link CassandraParameters} and
|
||||
* {@link ExpressionParser}.
|
||||
* Create a new {@link StringBasedQuery} given {@code query}, {@link CassandraParameters} and {@link ValueExpressionDelegate}.
|
||||
*
|
||||
* @param query must not be empty.
|
||||
* @param parameters must not be {@literal null}.
|
||||
* @param expressionParser must not be {@literal null}.
|
||||
*/
|
||||
StringBasedQuery(String query, CassandraParameters parameters, ExpressionParser expressionParser) {
|
||||
StringBasedQuery(String query, CassandraParameters parameters, ValueExpressionDelegate expressionParser) {
|
||||
|
||||
this.query = ParameterBindingParser.INSTANCE.parseAndCollectParameterBindingsFromQueryIntoBindings(query,
|
||||
this.queryParameterBindings);
|
||||
@@ -77,7 +77,7 @@ class StringBasedQuery {
|
||||
for (ParameterBinding binding : queryParameterBindings) {
|
||||
if (binding.isExpression()) {
|
||||
dependencies
|
||||
.add(ExpressionDependencies.discover(expressionParser.parseExpression(binding.getRequiredExpression())));
|
||||
.add(expressionParser.parse(binding.getRequiredExpression()).getExpressionDependencies());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -100,7 +100,7 @@ class StringBasedQuery {
|
||||
* @param evaluator must not be {@literal null}.
|
||||
* @return the bound String query containing formatted parameters.
|
||||
*/
|
||||
public SimpleStatement bindQuery(CassandraParameterAccessor parameterAccessor, SpELExpressionEvaluator evaluator) {
|
||||
SimpleStatement bindQuery(CassandraParameterAccessor parameterAccessor, ValueExpressionEvaluator evaluator) {
|
||||
|
||||
Assert.notNull(parameterAccessor, "CassandraParameterAccessor must not be null");
|
||||
Assert.notNull(evaluator, "SpELExpressionEvaluator must not be null");
|
||||
@@ -176,6 +176,10 @@ class StringBasedQuery {
|
||||
private static final Pattern NAMED_PARAMETER_BINDING_PATTERN = Pattern.compile("\\:(\\w+)");
|
||||
private static final Pattern INDEX_BASED_EXPRESSION_PATTERN = Pattern.compile("\\?\\#\\{");
|
||||
private static final Pattern NAME_BASED_EXPRESSION_PATTERN = Pattern.compile("\\:\\#\\{");
|
||||
private static final Pattern INDEX_BASED_PROPERTY_PLACEHOLDER_PATTERN = Pattern.compile("\\?\\$\\{");
|
||||
private static final Pattern NAME_BASED_PROPERTY_PLACEHOLDER_PATTERN = Pattern.compile("\\:\\$\\{");
|
||||
|
||||
private static final Set<Pattern> VALUE_EXPRESSION_PATTERNS = Set.of(INDEX_BASED_EXPRESSION_PATTERN, NAME_BASED_EXPRESSION_PATTERN, INDEX_BASED_PROPERTY_PLACEHOLDER_PATTERN, NAME_BASED_PROPERTY_PLACEHOLDER_PATTERN);
|
||||
|
||||
private static final String ARGUMENT_PLACEHOLDER = "?_param_?";
|
||||
|
||||
@@ -217,7 +221,7 @@ class StringBasedQuery {
|
||||
int exprStart = matcher.start();
|
||||
currentPosition = exprStart;
|
||||
|
||||
if (matcher.pattern() == NAME_BASED_EXPRESSION_PATTERN || matcher.pattern() == INDEX_BASED_EXPRESSION_PATTERN) {
|
||||
if (isValueExpression(matcher)) {
|
||||
// eat parameter expression
|
||||
int curlyBraceOpenCount = 1;
|
||||
currentPosition += 3;
|
||||
@@ -233,7 +237,6 @@ class StringBasedQuery {
|
||||
default:
|
||||
}
|
||||
}
|
||||
|
||||
result.append(input.subSequence(startIndex, exprStart));
|
||||
} else {
|
||||
result.append(input.subSequence(startIndex, exprStart));
|
||||
@@ -241,10 +244,10 @@ class StringBasedQuery {
|
||||
|
||||
result.append(ARGUMENT_PLACEHOLDER);
|
||||
|
||||
if (matcher.pattern() == NAME_BASED_EXPRESSION_PATTERN || matcher.pattern() == INDEX_BASED_EXPRESSION_PATTERN) {
|
||||
if (isValueExpression(matcher)) {
|
||||
bindings.add(
|
||||
BindingContext.ParameterBinding
|
||||
.expression(input.substring(exprStart + 3, currentPosition - 1), true));
|
||||
.expression(input.substring(exprStart + 1, currentPosition), true));
|
||||
} else {
|
||||
if (matcher.pattern() == INDEX_PARAMETER_BINDING_PATTERN) {
|
||||
bindings
|
||||
@@ -262,6 +265,10 @@ class StringBasedQuery {
|
||||
return result.append(input.subSequence(currentPosition, input.length())).toString();
|
||||
}
|
||||
|
||||
private static boolean isValueExpression(Matcher matcher) {
|
||||
return VALUE_EXPRESSION_PATTERNS.contains(matcher.pattern());
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static Matcher findNextBindingOrExpression(String input, int position) {
|
||||
|
||||
@@ -271,6 +278,8 @@ class StringBasedQuery {
|
||||
matchers.add(NAMED_PARAMETER_BINDING_PATTERN.matcher(input));
|
||||
matchers.add(INDEX_BASED_EXPRESSION_PATTERN.matcher(input));
|
||||
matchers.add(NAME_BASED_EXPRESSION_PATTERN.matcher(input));
|
||||
matchers.add(INDEX_BASED_PROPERTY_PLACEHOLDER_PATTERN.matcher(input));
|
||||
matchers.add(NAME_BASED_PROPERTY_PLACEHOLDER_PATTERN.matcher(input));
|
||||
|
||||
TreeMap<Integer, Matcher> matcherMap = new TreeMap<>();
|
||||
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
* Copyright 2024 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.cassandra.repository.query;
|
||||
|
||||
import java.util.function.Function;
|
||||
|
||||
import org.springframework.data.expression.ValueEvaluationContext;
|
||||
import org.springframework.data.expression.ValueExpression;
|
||||
import org.springframework.data.mapping.model.ValueExpressionEvaluator;
|
||||
import org.springframework.data.repository.query.ValueExpressionDelegate;
|
||||
|
||||
class ValueExpressionDelegateValueExpressionEvaluator implements ValueExpressionEvaluator {
|
||||
|
||||
private final ValueExpressionDelegate delegate;
|
||||
private final Function<ValueExpression, ValueEvaluationContext> expressionToContext;
|
||||
|
||||
ValueExpressionDelegateValueExpressionEvaluator(ValueExpressionDelegate delegate, Function<ValueExpression, ValueEvaluationContext> expressionToContext) {
|
||||
this.delegate = delegate;
|
||||
this.expressionToContext = expressionToContext;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public <T> T evaluate(String expressionString) {
|
||||
ValueExpression expression = delegate.parse(expressionString);
|
||||
return (T) expression.evaluate(expressionToContext.apply(expression));
|
||||
}
|
||||
}
|
||||
@@ -19,6 +19,7 @@ import java.lang.reflect.Method;
|
||||
import java.util.Optional;
|
||||
|
||||
import org.springframework.beans.factory.BeanFactory;
|
||||
import org.springframework.core.env.StandardEnvironment;
|
||||
import org.springframework.data.cassandra.core.CassandraOperations;
|
||||
import org.springframework.data.cassandra.core.mapping.CassandraPersistentEntity;
|
||||
import org.springframework.data.cassandra.core.mapping.CassandraPersistentProperty;
|
||||
@@ -27,6 +28,7 @@ import org.springframework.data.cassandra.repository.query.CassandraEntityInform
|
||||
import org.springframework.data.cassandra.repository.query.CassandraQueryMethod;
|
||||
import org.springframework.data.cassandra.repository.query.PartTreeCassandraQuery;
|
||||
import org.springframework.data.cassandra.repository.query.StringBasedCassandraQuery;
|
||||
import org.springframework.data.expression.ValueExpressionParser;
|
||||
import org.springframework.data.mapping.context.MappingContext;
|
||||
import org.springframework.data.projection.ProjectionFactory;
|
||||
import org.springframework.data.repository.core.NamedQueries;
|
||||
@@ -36,8 +38,9 @@ import org.springframework.data.repository.core.support.RepositoryFactorySupport
|
||||
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.QueryMethodValueEvaluationContextAccessor;
|
||||
import org.springframework.data.repository.query.RepositoryQuery;
|
||||
import org.springframework.expression.ExpressionParser;
|
||||
import org.springframework.data.repository.query.ValueExpressionDelegate;
|
||||
import org.springframework.expression.spel.standard.SpelExpressionParser;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.Assert;
|
||||
@@ -53,7 +56,7 @@ import org.springframework.util.Assert;
|
||||
*/
|
||||
public class CassandraRepositoryFactory extends RepositoryFactorySupport {
|
||||
|
||||
private static final SpelExpressionParser EXPRESSION_PARSER = new SpelExpressionParser();
|
||||
private static final ValueExpressionParser EXPRESSION_PARSER = ValueExpressionParser.create(SpelExpressionParser::new);
|
||||
|
||||
private final MappingContext<? extends CassandraPersistentEntity<?>, CassandraPersistentProperty> mappingContext;
|
||||
|
||||
@@ -99,29 +102,31 @@ public class CassandraRepositoryFactory extends RepositoryFactorySupport {
|
||||
return new MappingCassandraEntityInformation<>((CassandraPersistentEntity<T>) entity, operations.getConverter());
|
||||
}
|
||||
|
||||
@Override protected Optional<QueryLookupStrategy> getQueryLookupStrategy(Key key,
|
||||
ValueExpressionDelegate valueExpressionDelegate) {
|
||||
return Optional.of(new CassandraQueryLookupStrategy(operations, valueExpressionDelegate, mappingContext));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Optional<QueryLookupStrategy> getQueryLookupStrategy(@Nullable Key key,
|
||||
QueryMethodEvaluationContextProvider evaluationContextProvider) {
|
||||
|
||||
return Optional.of(new CassandraQueryLookupStrategy(operations, evaluationContextProvider, mappingContext));
|
||||
return Optional.of(new CassandraQueryLookupStrategy(operations, new ValueExpressionDelegate(new QueryMethodValueEvaluationContextAccessor(new StandardEnvironment(), evaluationContextProvider.getEvaluationContextProvider()), EXPRESSION_PARSER), mappingContext));
|
||||
}
|
||||
|
||||
private static class CassandraQueryLookupStrategy implements QueryLookupStrategy {
|
||||
|
||||
private final QueryMethodEvaluationContextProvider evaluationContextProvider;
|
||||
private final ValueExpressionDelegate valueExpressionDelegate;
|
||||
|
||||
private final MappingContext<? extends CassandraPersistentEntity<?>, CassandraPersistentProperty> mappingContext;
|
||||
|
||||
private final CassandraOperations operations;
|
||||
|
||||
private final ExpressionParser expressionParser = new CachingExpressionParser(EXPRESSION_PARSER);
|
||||
|
||||
CassandraQueryLookupStrategy(CassandraOperations operations,
|
||||
QueryMethodEvaluationContextProvider evaluationContextProvider,
|
||||
ValueExpressionDelegate valueExpressionDelegate,
|
||||
MappingContext<? extends CassandraPersistentEntity<?>, CassandraPersistentProperty> mappingContext) {
|
||||
|
||||
this.operations = operations;
|
||||
this.evaluationContextProvider = evaluationContextProvider;
|
||||
this.valueExpressionDelegate = valueExpressionDelegate;
|
||||
this.mappingContext = mappingContext;
|
||||
}
|
||||
|
||||
@@ -134,10 +139,9 @@ public class CassandraRepositoryFactory extends RepositoryFactorySupport {
|
||||
|
||||
if (namedQueries.hasQuery(namedQueryName)) {
|
||||
String namedQuery = namedQueries.getQuery(namedQueryName);
|
||||
return new StringBasedCassandraQuery(namedQuery, queryMethod, operations, expressionParser,
|
||||
evaluationContextProvider);
|
||||
return new StringBasedCassandraQuery(namedQuery, queryMethod, operations, valueExpressionDelegate);
|
||||
} else if (queryMethod.hasAnnotatedQuery()) {
|
||||
return new StringBasedCassandraQuery(queryMethod, operations, expressionParser, evaluationContextProvider);
|
||||
return new StringBasedCassandraQuery(queryMethod, operations, valueExpressionDelegate);
|
||||
} else {
|
||||
return new PartTreeCassandraQuery(queryMethod, operations);
|
||||
}
|
||||
|
||||
@@ -19,6 +19,7 @@ import java.lang.reflect.Method;
|
||||
import java.util.Optional;
|
||||
|
||||
import org.springframework.beans.factory.BeanFactory;
|
||||
import org.springframework.core.env.StandardEnvironment;
|
||||
import org.springframework.data.cassandra.core.ReactiveCassandraOperations;
|
||||
import org.springframework.data.cassandra.core.mapping.CassandraPersistentEntity;
|
||||
import org.springframework.data.cassandra.core.mapping.CassandraPersistentProperty;
|
||||
@@ -26,6 +27,7 @@ import org.springframework.data.cassandra.repository.query.CassandraEntityInform
|
||||
import org.springframework.data.cassandra.repository.query.ReactiveCassandraQueryMethod;
|
||||
import org.springframework.data.cassandra.repository.query.ReactivePartTreeCassandraQuery;
|
||||
import org.springframework.data.cassandra.repository.query.ReactiveStringBasedCassandraQuery;
|
||||
import org.springframework.data.expression.ValueExpressionParser;
|
||||
import org.springframework.data.mapping.context.MappingContext;
|
||||
import org.springframework.data.projection.ProjectionFactory;
|
||||
import org.springframework.data.repository.core.NamedQueries;
|
||||
@@ -35,9 +37,10 @@ import org.springframework.data.repository.core.support.ReactiveRepositoryFactor
|
||||
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.QueryMethodValueEvaluationContextAccessor;
|
||||
import org.springframework.data.repository.query.ReactiveQueryMethodEvaluationContextProvider;
|
||||
import org.springframework.data.repository.query.RepositoryQuery;
|
||||
import org.springframework.expression.ExpressionParser;
|
||||
import org.springframework.data.repository.query.ValueExpressionDelegate;
|
||||
import org.springframework.expression.spel.standard.SpelExpressionParser;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.Assert;
|
||||
@@ -50,7 +53,7 @@ import org.springframework.util.Assert;
|
||||
*/
|
||||
public class ReactiveCassandraRepositoryFactory extends ReactiveRepositoryFactorySupport {
|
||||
|
||||
private static final SpelExpressionParser EXPRESSION_PARSER = new SpelExpressionParser();
|
||||
private static final ValueExpressionParser EXPRESSION_PARSER = ValueExpressionParser.create(SpelExpressionParser::new);
|
||||
|
||||
private final ReactiveCassandraOperations operations;
|
||||
|
||||
@@ -89,11 +92,15 @@ public class ReactiveCassandraRepositoryFactory extends ReactiveRepositoryFactor
|
||||
return getTargetRepositoryViaReflection(information, entityInformation, operations);
|
||||
}
|
||||
|
||||
@Override protected Optional<QueryLookupStrategy> getQueryLookupStrategy(Key key,
|
||||
ValueExpressionDelegate valueExpressionDelegate) {
|
||||
return Optional.of(new CassandraQueryLookupStrategy(operations, valueExpressionDelegate, mappingContext));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Optional<QueryLookupStrategy> getQueryLookupStrategy(@Nullable Key key,
|
||||
QueryMethodEvaluationContextProvider evaluationContextProvider) {
|
||||
return Optional.of(new CassandraQueryLookupStrategy(operations,
|
||||
(ReactiveQueryMethodEvaluationContextProvider) evaluationContextProvider, mappingContext));
|
||||
return Optional.of(new CassandraQueryLookupStrategy(operations, new ValueExpressionDelegate(new QueryMethodValueEvaluationContextAccessor(new StandardEnvironment(), evaluationContextProvider.getEvaluationContextProvider()), EXPRESSION_PARSER), mappingContext));
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@@ -112,19 +119,17 @@ public class ReactiveCassandraRepositoryFactory extends ReactiveRepositoryFactor
|
||||
*/
|
||||
private static class CassandraQueryLookupStrategy implements QueryLookupStrategy {
|
||||
|
||||
private final ReactiveQueryMethodEvaluationContextProvider evaluationContextProvider;
|
||||
private final ValueExpressionDelegate delegate;
|
||||
|
||||
private final ReactiveCassandraOperations operations;
|
||||
|
||||
private final MappingContext<? extends CassandraPersistentEntity<?>, ? extends CassandraPersistentProperty> mappingContext;
|
||||
|
||||
private final ExpressionParser expressionParser = new CachingExpressionParser(EXPRESSION_PARSER);
|
||||
|
||||
CassandraQueryLookupStrategy(ReactiveCassandraOperations operations,
|
||||
ReactiveQueryMethodEvaluationContextProvider evaluationContextProvider,
|
||||
ValueExpressionDelegate delegate,
|
||||
MappingContext<? extends CassandraPersistentEntity<?>, ? extends CassandraPersistentProperty> mappingContext) {
|
||||
|
||||
this.evaluationContextProvider = evaluationContextProvider;
|
||||
this.delegate = delegate;
|
||||
this.operations = operations;
|
||||
this.mappingContext = mappingContext;
|
||||
}
|
||||
@@ -141,11 +146,9 @@ public class ReactiveCassandraRepositoryFactory extends ReactiveRepositoryFactor
|
||||
if (namedQueries.hasQuery(namedQueryName)) {
|
||||
String namedQuery = namedQueries.getQuery(namedQueryName);
|
||||
|
||||
return new ReactiveStringBasedCassandraQuery(namedQuery, queryMethod, operations, expressionParser,
|
||||
evaluationContextProvider);
|
||||
return new ReactiveStringBasedCassandraQuery(namedQuery, queryMethod, operations, delegate);
|
||||
} else if (queryMethod.hasAnnotatedQuery()) {
|
||||
return new ReactiveStringBasedCassandraQuery(queryMethod, operations, expressionParser,
|
||||
evaluationContextProvider);
|
||||
return new ReactiveStringBasedCassandraQuery(queryMethod, operations, delegate);
|
||||
} else {
|
||||
return new ReactivePartTreeCassandraQuery(queryMethod, operations);
|
||||
}
|
||||
|
||||
@@ -28,6 +28,7 @@ import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
|
||||
import org.springframework.data.cassandra.ReactiveSession;
|
||||
import org.springframework.data.cassandra.core.ReactiveCassandraOperations;
|
||||
import org.springframework.data.cassandra.core.convert.MappingCassandraConverter;
|
||||
@@ -37,16 +38,20 @@ import org.springframework.data.cassandra.core.mapping.CassandraMappingContext;
|
||||
import org.springframework.data.cassandra.domain.Person;
|
||||
import org.springframework.data.cassandra.repository.Consistency;
|
||||
import org.springframework.data.cassandra.repository.Query;
|
||||
import org.springframework.data.expression.ValueExpressionParser;
|
||||
import org.springframework.data.projection.ProjectionFactory;
|
||||
import org.springframework.data.projection.SpelAwareProxyProjectionFactory;
|
||||
import org.springframework.data.repository.Repository;
|
||||
import org.springframework.data.repository.core.RepositoryMetadata;
|
||||
import org.springframework.data.repository.core.support.AbstractRepositoryMetadata;
|
||||
import org.springframework.data.repository.query.QueryMethodValueEvaluationContextAccessor;
|
||||
import org.springframework.data.repository.query.ReactiveExtensionAwareQueryMethodEvaluationContextProvider;
|
||||
import org.springframework.data.repository.query.ValueExpressionDelegate;
|
||||
import org.springframework.data.spel.spi.EvaluationContextExtension;
|
||||
import org.springframework.data.spel.spi.ReactiveEvaluationContextExtension;
|
||||
import org.springframework.expression.spel.standard.SpelExpressionParser;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.mock.env.MockEnvironment;
|
||||
import org.springframework.util.ReflectionUtils;
|
||||
|
||||
import com.datastax.oss.driver.api.core.DefaultConsistencyLevel;
|
||||
@@ -60,7 +65,7 @@ import com.datastax.oss.driver.api.core.cql.SimpleStatement;
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
class ReactiveStringBasedCassandraQueryUnitTests {
|
||||
|
||||
private static final SpelExpressionParser PARSER = new SpelExpressionParser();
|
||||
private static final ValueExpressionParser PARSER = ValueExpressionParser.create(SpelExpressionParser::new);
|
||||
|
||||
@Mock private ReactiveCassandraOperations operations;
|
||||
@Mock private ReactiveCqlOperations cqlOperations;
|
||||
@@ -69,6 +74,7 @@ class ReactiveStringBasedCassandraQueryUnitTests {
|
||||
private MappingCassandraConverter converter;
|
||||
private ProjectionFactory factory;
|
||||
private RepositoryMetadata metadata;
|
||||
private MockEnvironment mockEnvironment = new MockEnvironment();
|
||||
|
||||
@BeforeEach
|
||||
@SuppressWarnings("unchecked")
|
||||
@@ -143,6 +149,20 @@ class ReactiveStringBasedCassandraQueryUnitTests {
|
||||
assertThat(actual.getPositionalValues().get(0)).isEqualTo("Walter");
|
||||
}
|
||||
|
||||
@Test // GH-1522
|
||||
void shouldUsePropertyPlaceholder() {
|
||||
mockEnvironment.withProperty("someProp", "Walter");
|
||||
ReactiveStringBasedCassandraQuery cassandraQuery = getQueryMethod("findByPropertyPlaceholder");
|
||||
|
||||
CassandraParametersParameterAccessor parameterAccessor = new CassandraParametersParameterAccessor(
|
||||
cassandraQuery.getQueryMethod());
|
||||
|
||||
SimpleStatement actual = cassandraQuery.createQuery(parameterAccessor).block();
|
||||
|
||||
assertThat(actual.getQuery()).isEqualTo("SELECT * FROM person WHERE lastname=?;");
|
||||
assertThat(actual.getPositionalValues().get(0)).isEqualTo("Walter");
|
||||
}
|
||||
|
||||
private ReactiveStringBasedCassandraQuery getQueryMethod(String name, Class<?>... args) {
|
||||
|
||||
Method method = ReflectionUtils.findMethod(SampleRepository.class, name, args);
|
||||
@@ -150,11 +170,10 @@ class ReactiveStringBasedCassandraQueryUnitTests {
|
||||
ReactiveCassandraQueryMethod queryMethod = new ReactiveCassandraQueryMethod(method, metadata, factory,
|
||||
converter.getMappingContext());
|
||||
|
||||
ReactiveExtensionAwareQueryMethodEvaluationContextProvider provider = new ReactiveExtensionAwareQueryMethodEvaluationContextProvider(
|
||||
Arrays.asList(MyReactiveExtension.INSTANCE, MyDefunctExtension.INSTANCE));
|
||||
QueryMethodValueEvaluationContextAccessor accessor = new QueryMethodValueEvaluationContextAccessor(
|
||||
mockEnvironment, Arrays.asList(MyReactiveExtension.INSTANCE, MyDefunctExtension.INSTANCE));
|
||||
|
||||
return new ReactiveStringBasedCassandraQuery(queryMethod, operations, PARSER,
|
||||
provider);
|
||||
return new ReactiveStringBasedCassandraQuery(queryMethod, operations, new ValueExpressionDelegate(accessor, PARSER));
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
@@ -170,6 +189,9 @@ class ReactiveStringBasedCassandraQueryUnitTests {
|
||||
@Query("SELECT * FROM person WHERE lastname=:#{getName()};")
|
||||
Person findBySpel();
|
||||
|
||||
@Query("SELECT * FROM person WHERE lastname=:${someProp};")
|
||||
Person findByPropertyPlaceholder();
|
||||
|
||||
}
|
||||
|
||||
public static class MyReactiveExtensionObject implements EvaluationContextExtension {
|
||||
|
||||
@@ -25,6 +25,7 @@ import java.nio.ByteBuffer;
|
||||
import java.time.LocalDate;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
@@ -33,6 +34,7 @@ import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
|
||||
import org.springframework.data.cassandra.core.CassandraOperations;
|
||||
import org.springframework.data.cassandra.core.convert.MappingCassandraConverter;
|
||||
import org.springframework.data.cassandra.core.cql.QueryOptions;
|
||||
@@ -43,15 +45,18 @@ import org.springframework.data.cassandra.domain.Person;
|
||||
import org.springframework.data.cassandra.repository.Consistency;
|
||||
import org.springframework.data.cassandra.repository.Query;
|
||||
import org.springframework.data.cassandra.support.UserDefinedTypeBuilder;
|
||||
import org.springframework.data.expression.ValueExpressionParser;
|
||||
import org.springframework.data.projection.ProjectionFactory;
|
||||
import org.springframework.data.projection.SpelAwareProxyProjectionFactory;
|
||||
import org.springframework.data.repository.Repository;
|
||||
import org.springframework.data.repository.core.RepositoryMetadata;
|
||||
import org.springframework.data.repository.core.support.AbstractRepositoryMetadata;
|
||||
import org.springframework.data.repository.query.ExtensionAwareQueryMethodEvaluationContextProvider;
|
||||
import org.springframework.data.repository.query.Param;
|
||||
import org.springframework.data.repository.query.QueryCreationException;
|
||||
import org.springframework.data.repository.query.QueryMethodValueEvaluationContextAccessor;
|
||||
import org.springframework.data.repository.query.ValueExpressionDelegate;
|
||||
import org.springframework.expression.spel.standard.SpelExpressionParser;
|
||||
import org.springframework.mock.env.MockEnvironment;
|
||||
import org.springframework.util.ReflectionUtils;
|
||||
|
||||
import com.datastax.oss.driver.api.core.CqlIdentifier;
|
||||
@@ -71,7 +76,7 @@ import com.datastax.oss.driver.api.core.type.UserDefinedType;
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
class StringBasedCassandraQueryUnitTests {
|
||||
|
||||
private static final SpelExpressionParser PARSER = new SpelExpressionParser();
|
||||
private static final ValueExpressionParser PARSER = ValueExpressionParser.create(SpelExpressionParser::new);
|
||||
|
||||
@Mock private CassandraOperations operations;
|
||||
@Mock private UdtValue udtValue;
|
||||
@@ -80,6 +85,7 @@ class StringBasedCassandraQueryUnitTests {
|
||||
private RepositoryMetadata metadata;
|
||||
private MappingCassandraConverter converter;
|
||||
private ProjectionFactory factory;
|
||||
private MockEnvironment environment = new MockEnvironment();
|
||||
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
@@ -293,6 +299,20 @@ class StringBasedCassandraQueryUnitTests {
|
||||
assertThat(actual.getPositionalValues().get(0)).isEqualTo("Walter");
|
||||
}
|
||||
|
||||
@Test // GH-1522
|
||||
void bindsPropertyPlaceholderParameterCorrectly() {
|
||||
environment.withProperty("someParam", "Walter");
|
||||
|
||||
StringBasedCassandraQuery cassandraQuery = getQueryMethod("findByPropertyPlaceholder");
|
||||
CassandraParametersParameterAccessor accessor = new CassandraParametersParameterAccessor(
|
||||
cassandraQuery.getQueryMethod());
|
||||
|
||||
SimpleStatement actual = cassandraQuery.createQuery(accessor);
|
||||
|
||||
assertThat(actual.getQuery()).isEqualTo("SELECT * FROM person WHERE lastname = ?;");
|
||||
assertThat(actual.getPositionalValues().get(0)).isEqualTo("Walter");
|
||||
}
|
||||
|
||||
@Test // DATACASS-117
|
||||
void bindsReusedParametersCorrectly() {
|
||||
|
||||
@@ -431,8 +451,10 @@ class StringBasedCassandraQueryUnitTests {
|
||||
CassandraQueryMethod queryMethod = new CassandraQueryMethod(method, metadata, factory,
|
||||
converter.getMappingContext());
|
||||
|
||||
return new StringBasedCassandraQuery(queryMethod, operations, PARSER,
|
||||
ExtensionAwareQueryMethodEvaluationContextProvider.DEFAULT);
|
||||
QueryMethodValueEvaluationContextAccessor accessor = new QueryMethodValueEvaluationContextAccessor(
|
||||
environment, Collections.emptySet());
|
||||
|
||||
return new StringBasedCassandraQuery(queryMethod, operations, new ValueExpressionDelegate(accessor, PARSER));
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
@@ -480,6 +502,9 @@ class StringBasedCassandraQueryUnitTests {
|
||||
@Query("SELECT * FROM person WHERE lastname = :#{#lastname == 'Matthews' ? 'Woohoo' : #lastname};")
|
||||
Person findByConditionalExpressionParameter(@Param("lastname") String lastname);
|
||||
|
||||
@Query("SELECT * FROM person WHERE lastname = :${someParam};")
|
||||
Person findByPropertyPlaceholder();
|
||||
|
||||
@Query("SELECT * FROM person WHERE lastname=?0 AND firstname=?1;")
|
||||
Person findByLastnameAndFirstname(String lastname, String firstname);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user