Skip to content

Commit

Permalink
Added support for ValueExpressions
Browse files Browse the repository at this point in the history
fixes #1522
  • Loading branch information
marcingrzejszczak committed Sep 30, 2024
1 parent 7902cef commit afdc791
Show file tree
Hide file tree
Showing 10 changed files with 270 additions and 91 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
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;
Expand All @@ -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;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@
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;
Expand Down Expand Up @@ -249,7 +249,7 @@ private boolean allowsFiltering() {
* @return the {@link Statement}.
*/
SimpleStatement select(StringBasedQuery stringBasedQuery, CassandraParameterAccessor parameterAccessor,
SpELExpressionEvaluator evaluator) {
ValueExpressionEvaluator evaluator) {

try {

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,17 @@

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;
Expand Down Expand Up @@ -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},
Expand All @@ -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) {
Expand All @@ -86,19 +95,59 @@ public ReactiveStringBasedCassandraQuery(ReactiveCassandraQueryMethod queryMetho
* {@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()) {

Expand Down Expand Up @@ -126,10 +175,9 @@ public Mono<SimpleStatement> createQuery(CassandraParameterAccessor parameterAcc
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
Expand All @@ -152,21 +200,9 @@ protected boolean isModifyingQuery() {
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));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -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},
Expand All @@ -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()) {

Expand All @@ -106,6 +126,26 @@ public StringBasedCassandraQuery(String query, CassandraQueryMethod method, Cass
}
}

/**
* 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;
}
Expand All @@ -116,11 +156,10 @@ public SimpleStatement createQuery(CassandraParameterAccessor parameterAccessor)
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
Expand Down
Loading

0 comments on commit afdc791

Please sign in to comment.