Skip to content

Commit e219fc9

Browse files
Add support for Value Expressions for Repository Query methods.
Original pull request #4683 Closes #4677
1 parent 414ef64 commit e219fc9

14 files changed

+285
-197
lines changed

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

+48-16
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,9 @@
2121
import org.bson.Document;
2222
import org.bson.codecs.configuration.CodecRegistry;
2323

24+
import org.springframework.core.env.StandardEnvironment;
2425
import org.springframework.data.expression.ValueEvaluationContext;
26+
import org.springframework.data.expression.ValueEvaluationContextProvider;
2527
import org.springframework.data.expression.ValueExpression;
2628
import org.springframework.data.expression.ValueExpressionParser;
2729
import org.springframework.data.mapping.model.SpELExpressionEvaluator;
@@ -46,13 +48,15 @@
4648
import org.springframework.data.mongodb.util.json.ParameterBindingContext;
4749
import org.springframework.data.mongodb.util.json.ParameterBindingDocumentCodec;
4850
import org.springframework.data.repository.query.ParameterAccessor;
49-
import org.springframework.data.repository.query.QueryMethodValueEvaluationContextProvider;
51+
import org.springframework.data.repository.query.QueryMethodEvaluationContextProvider;
52+
import org.springframework.data.repository.query.QueryMethodValueEvaluationContextAccessor;
5053
import org.springframework.data.repository.query.RepositoryQuery;
5154
import org.springframework.data.repository.query.ResultProcessor;
52-
import org.springframework.data.repository.query.ValueExpressionSupportHolder;
55+
import org.springframework.data.repository.query.ValueExpressionDelegate;
5356
import org.springframework.data.spel.ExpressionDependencies;
5457
import org.springframework.data.util.Lazy;
5558
import org.springframework.expression.EvaluationContext;
59+
import org.springframework.expression.ExpressionParser;
5660
import org.springframework.expression.spel.standard.SpelExpressionParser;
5761
import org.springframework.lang.Nullable;
5862
import org.springframework.util.Assert;
@@ -76,24 +80,28 @@ public abstract class AbstractMongoQuery implements RepositoryQuery {
7680
private final MongoOperations operations;
7781
private final ExecutableFind<?> executableFind;
7882
private final ExecutableUpdate<?> executableUpdate;
79-
private final ValueExpressionParser expressionParser;
80-
private final QueryMethodValueEvaluationContextProvider evaluationContextProvider;
8183
private final Lazy<ParameterBindingDocumentCodec> codec = Lazy
8284
.of(() -> new ParameterBindingDocumentCodec(getCodecRegistry()));
85+
private final ValueExpressionDelegate valueExpressionDelegate;
86+
private final ValueEvaluationContextProvider valueEvaluationContextProvider;
8387

8488
/**
8589
* Creates a new {@link AbstractMongoQuery} from the given {@link MongoQueryMethod} and {@link MongoOperations}.
8690
*
8791
* @param method must not be {@literal null}.
8892
* @param operations must not be {@literal null}.
89-
* @param expressionSupportHolder must not be {@literal null}.
93+
* @param expressionParser must not be {@literal null}.
94+
* @param evaluationContextProvider must not be {@literal null}.
95+
* @deprecated use the constructor version with {@link ValueExpressionDelegate}
9096
*/
91-
public AbstractMongoQuery(MongoQueryMethod method, MongoOperations operations,
92-
ValueExpressionSupportHolder expressionSupportHolder) {
97+
@Deprecated(since = "4.4.0")
98+
public AbstractMongoQuery(MongoQueryMethod method, MongoOperations operations, ExpressionParser expressionParser,
99+
QueryMethodEvaluationContextProvider evaluationContextProvider) {
93100

94101
Assert.notNull(operations, "MongoOperations must not be null");
95102
Assert.notNull(method, "MongoQueryMethod must not be null");
96-
Assert.notNull(expressionSupportHolder, "ValueExpressionSupportHolder must not be null");
103+
Assert.notNull(expressionParser, "SpelExpressionParser must not be null");
104+
Assert.notNull(evaluationContextProvider, "QueryMethodEvaluationContextProvider must not be null");
97105

98106
this.method = method;
99107
this.operations = operations;
@@ -103,8 +111,32 @@ public AbstractMongoQuery(MongoQueryMethod method, MongoOperations operations,
103111

104112
this.executableFind = operations.query(type);
105113
this.executableUpdate = operations.update(type);
106-
this.expressionParser = expressionSupportHolder;
107-
this.evaluationContextProvider = expressionSupportHolder.createValueContextProvider(method.getParameters());
114+
this.valueExpressionDelegate = new ValueExpressionDelegate(new QueryMethodValueEvaluationContextAccessor(new StandardEnvironment(), evaluationContextProvider.getEvaluationContextProvider()), ValueExpressionParser.create(() -> expressionParser));
115+
this.valueEvaluationContextProvider = valueExpressionDelegate.createValueContextProvider(method.getParameters());
116+
}
117+
118+
/**
119+
* Creates a new {@link AbstractMongoQuery} from the given {@link MongoQueryMethod} and {@link MongoOperations}.
120+
*
121+
* @param method must not be {@literal null}.
122+
* @param operations must not be {@literal null}.
123+
* @param delegate must not be {@literal null}
124+
*/
125+
public AbstractMongoQuery(MongoQueryMethod method, MongoOperations operations, ValueExpressionDelegate delegate) {
126+
127+
Assert.notNull(operations, "MongoOperations must not be null");
128+
Assert.notNull(method, "MongoQueryMethod must not be null");
129+
130+
this.method = method;
131+
this.operations = operations;
132+
133+
MongoEntityMetadata<?> metadata = method.getEntityInformation();
134+
Class<?> type = metadata.getCollectionEntity().getType();
135+
136+
this.executableFind = operations.query(type);
137+
this.executableUpdate = operations.update(type);
138+
this.valueExpressionDelegate = delegate;
139+
this.valueEvaluationContextProvider = delegate.createValueContextProvider(method.getParameters());
108140
}
109141

110142
@Override
@@ -375,8 +407,7 @@ protected ParameterBindingDocumentCodec getParameterBindingCodec() {
375407
protected SpELExpressionEvaluator getSpELExpressionEvaluatorFor(ExpressionDependencies dependencies,
376408
ConvertingParameterAccessor accessor) {
377409

378-
return new DefaultSpELExpressionEvaluator(new SpelExpressionParser(),
379-
evaluationContextProvider.getEvaluationContext(accessor.getValues(), dependencies).getEvaluationContext());
410+
return new DefaultSpELExpressionEvaluator(new SpelExpressionParser(), valueEvaluationContextProvider.getEvaluationContext(accessor.getValues(), dependencies).getEvaluationContext());
380411
}
381412

382413
/**
@@ -393,11 +424,12 @@ protected ValueExpressionEvaluator getExpressionEvaluatorFor(MongoParameterAcces
393424
@Override
394425
public <T> T evaluate(String expressionString) {
395426

396-
ValueExpression expression = expressionParser.parse(expressionString);
397-
ValueEvaluationContext evaluationContext = evaluationContextProvider.getEvaluationContext(accessor.getValues(),
398-
expression.getExpressionDependencies());
427+
ValueExpression expression = valueExpressionDelegate.parse(expressionString);
428+
ValueEvaluationContext evaluationContext =
429+
valueEvaluationContextProvider.getEvaluationContext(accessor.getValues(), expression.getExpressionDependencies());
430+
431+
return (T) expression.evaluate(evaluationContext);
399432

400-
return (T) expression.evaluate(evaluationContext);
401433
}
402434
};
403435
}

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

+69-27
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,12 @@
2626
import org.reactivestreams.Publisher;
2727

2828
import org.springframework.core.convert.converter.Converter;
29+
import org.springframework.core.env.StandardEnvironment;
30+
import org.springframework.data.expression.ReactiveValueEvaluationContextProvider;
31+
import org.springframework.data.expression.ValueEvaluationContext;
32+
import org.springframework.data.expression.ValueEvaluationContextProvider;
2933
import org.springframework.data.expression.ValueExpression;
34+
import org.springframework.data.expression.ValueExpressionParser;
3035
import org.springframework.data.mapping.model.EntityInstantiators;
3136
import org.springframework.data.mapping.model.SpELExpressionEvaluator;
3237
import org.springframework.data.mapping.model.ValueExpressionEvaluator;
@@ -50,12 +55,14 @@
5055
import org.springframework.data.mongodb.util.json.ParameterBindingContext;
5156
import org.springframework.data.mongodb.util.json.ParameterBindingDocumentCodec;
5257
import org.springframework.data.repository.query.ParameterAccessor;
53-
import org.springframework.data.repository.query.ReactiveQueryMethodValueEvaluationContextProvider;
58+
import org.springframework.data.repository.query.QueryMethodValueEvaluationContextAccessor;
59+
import org.springframework.data.repository.query.ReactiveQueryMethodEvaluationContextProvider;
5460
import org.springframework.data.repository.query.RepositoryQuery;
5561
import org.springframework.data.repository.query.ResultProcessor;
56-
import org.springframework.data.repository.query.ValueExpressionSupportHolder;
62+
import org.springframework.data.repository.query.ValueExpressionDelegate;
5763
import org.springframework.data.spel.ExpressionDependencies;
5864
import org.springframework.data.util.TypeInformation;
65+
import org.springframework.expression.ExpressionParser;
5966
import org.springframework.expression.spel.standard.SpelExpressionParser;
6067
import org.springframework.lang.Nullable;
6168
import org.springframework.util.Assert;
@@ -80,36 +87,72 @@ public abstract class AbstractReactiveMongoQuery implements RepositoryQuery {
8087
private final EntityInstantiators instantiators;
8188
private final FindWithProjection<?> findOperationWithProjection;
8289
private final ReactiveUpdate<?> updateOps;
83-
private final ValueExpressionSupportHolder expressionSupportHolder;
84-
private final ReactiveQueryMethodValueEvaluationContextProvider evaluationContextProvider;
90+
private final ValueExpressionDelegate valueExpressionDelegate;
91+
private final ReactiveValueEvaluationContextProvider valueEvaluationContextProvider;
8592

8693
/**
8794
* Creates a new {@link AbstractReactiveMongoQuery} from the given {@link MongoQueryMethod} and
8895
* {@link MongoOperations}.
8996
*
9097
* @param method must not be {@literal null}.
9198
* @param operations must not be {@literal null}.
92-
* @param expressionSupportHolder must not be {@literal null}.
99+
* @param expressionParser must not be {@literal null}.
100+
* @param evaluationContextProvider must not be {@literal null}.
101+
* @deprecated use the constructor version with {@link ValueExpressionDelegate}
93102
*/
103+
@Deprecated(since = "4.4.0")
94104
public AbstractReactiveMongoQuery(ReactiveMongoQueryMethod method, ReactiveMongoOperations operations,
95-
ValueExpressionSupportHolder expressionSupportHolder) {
105+
ExpressionParser expressionParser, ReactiveQueryMethodEvaluationContextProvider evaluationContextProvider) {
96106

97107
Assert.notNull(method, "MongoQueryMethod must not be null");
98108
Assert.notNull(operations, "ReactiveMongoOperations must not be null");
99-
Assert.notNull(expressionSupportHolder, "ValueExpressionSupportHolder must not be null");
109+
Assert.notNull(expressionParser, "SpelExpressionParser must not be null");
110+
Assert.notNull(evaluationContextProvider, "ReactiveEvaluationContextExtension must not be null");
100111

101112
this.method = method;
102113
this.operations = operations;
103114
this.instantiators = new EntityInstantiators();
104-
this.expressionSupportHolder = expressionSupportHolder;
105-
this.evaluationContextProvider = (ReactiveQueryMethodValueEvaluationContextProvider) expressionSupportHolder
106-
.createValueContextProvider(method.getParameters());
115+
this.valueExpressionDelegate = new ValueExpressionDelegate(new QueryMethodValueEvaluationContextAccessor(new StandardEnvironment(), evaluationContextProvider.getEvaluationContextProvider()), ValueExpressionParser.create(() -> expressionParser));
107116

108117
MongoEntityMetadata<?> metadata = method.getEntityInformation();
109118
Class<?> type = metadata.getCollectionEntity().getType();
110119

111120
this.findOperationWithProjection = operations.query(type);
112121
this.updateOps = operations.update(type);
122+
ValueEvaluationContextProvider valueContextProvider = valueExpressionDelegate.createValueContextProvider(
123+
method.getParameters());
124+
Assert.isInstanceOf(ReactiveValueEvaluationContextProvider.class, valueContextProvider, "ValueEvaluationContextProvider must be reactive");
125+
this.valueEvaluationContextProvider = (ReactiveValueEvaluationContextProvider) valueContextProvider;
126+
}
127+
/**
128+
* Creates a new {@link AbstractReactiveMongoQuery} from the given {@link MongoQueryMethod} and
129+
* {@link MongoOperations}.
130+
*
131+
* @param method must not be {@literal null}.
132+
* @param operations must not be {@literal null}.
133+
* @param delegate must not be {@literal null}.
134+
*/
135+
public AbstractReactiveMongoQuery(ReactiveMongoQueryMethod method, ReactiveMongoOperations operations,
136+
ValueExpressionDelegate delegate) {
137+
138+
Assert.notNull(method, "MongoQueryMethod must not be null");
139+
Assert.notNull(operations, "ReactiveMongoOperations must not be null");
140+
Assert.notNull(delegate, "ValueExpressionDelegate must not be null");
141+
142+
this.method = method;
143+
this.operations = operations;
144+
this.instantiators = new EntityInstantiators();
145+
this.valueExpressionDelegate = delegate;
146+
147+
MongoEntityMetadata<?> metadata = method.getEntityInformation();
148+
Class<?> type = metadata.getCollectionEntity().getType();
149+
150+
this.findOperationWithProjection = operations.query(type);
151+
this.updateOps = operations.update(type);
152+
ValueEvaluationContextProvider valueContextProvider = valueExpressionDelegate.createValueContextProvider(
153+
method.getParameters());
154+
Assert.isInstanceOf(ReactiveValueEvaluationContextProvider.class, valueContextProvider, "ValueEvaluationContextProvider must be reactive");
155+
this.valueEvaluationContextProvider = (ReactiveValueEvaluationContextProvider) valueContextProvider;
113156
}
114157

115158
@Override
@@ -390,7 +433,7 @@ private Mono<Tuple2<ValueExpressionEvaluator, ParameterBindingDocumentCodec>> ex
390433
MongoParameterAccessor accessor, ParameterBindingDocumentCodec codec) {
391434

392435
ExpressionDependencies dependencies = codec.captureExpressionDependencies(source, accessor::getBindableValue,
393-
expressionSupportHolder.getValueExpressionParser());
436+
valueExpressionDelegate.getValueExpressionParser());
394437
return getValueExpressionEvaluatorLater(dependencies, accessor).zipWith(Mono.just(codec));
395438
}
396439

@@ -426,8 +469,7 @@ protected Mono<ParameterBindingDocumentCodec> getParameterBindingCodec() {
426469
@Deprecated(since = "4.3")
427470
protected Mono<SpELExpressionEvaluator> getSpelEvaluatorFor(ExpressionDependencies dependencies,
428471
MongoParameterAccessor accessor) {
429-
430-
return evaluationContextProvider.getEvaluationContextLater(accessor.getValues(), dependencies)
472+
return valueEvaluationContextProvider.getEvaluationContextLater(accessor.getValues(), dependencies)
431473
.map(evaluationContext -> (SpELExpressionEvaluator) new DefaultSpELExpressionEvaluator(
432474
new SpelExpressionParser(), evaluationContext.getEvaluationContext()))
433475
.defaultIfEmpty(DefaultSpELExpressionEvaluator.unsupported());
@@ -445,10 +487,10 @@ ValueExpressionEvaluator getValueExpressionEvaluator(MongoParameterAccessor acce
445487

446488
@Override
447489
public <T> T evaluate(String expressionString) {
448-
449-
ValueExpression expression = expressionSupportHolder.parse(expressionString);
450-
return (T) expression.evaluate(evaluationContextProvider.getEvaluationContext(accessor.getValues(),
451-
expression.getExpressionDependencies()));
490+
ValueExpression expression = valueExpressionDelegate.parse(expressionString);
491+
ValueEvaluationContext evaluationContext = valueEvaluationContextProvider.getEvaluationContext(accessor.getValues(),
492+
expression.getExpressionDependencies());
493+
return (T) expression.evaluate(evaluationContext);
452494
}
453495
};
454496
}
@@ -465,19 +507,19 @@ public <T> T evaluate(String expressionString) {
465507
protected Mono<ValueExpressionEvaluator> getValueExpressionEvaluatorLater(ExpressionDependencies dependencies,
466508
MongoParameterAccessor accessor) {
467509

468-
return evaluationContextProvider.getEvaluationContextLater(accessor.getValues(), dependencies)
469-
.map(evaluationContext -> {
510+
return valueEvaluationContextProvider.getEvaluationContextLater(accessor.getValues(), dependencies)
511+
.map(evaluationContext -> {
470512

471-
return new ValueExpressionEvaluator() {
472-
@Override
473-
public <T> T evaluate(String expressionString) {
513+
return new ValueExpressionEvaluator() {
514+
@Override
515+
public <T> T evaluate(String expressionString) {
474516

475-
ValueExpression expression = expressionSupportHolder.parse(expressionString);
517+
ValueExpression expression = valueExpressionDelegate.parse(expressionString);
476518

477-
return (T) expression.evaluate(evaluationContext);
478-
}
479-
};
480-
});
519+
return (T) expression.evaluate(evaluationContext);
520+
}
521+
};
522+
});
481523
}
482524

483525
/**

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

+12-10
Original file line numberDiff line numberDiff line change
@@ -30,11 +30,11 @@
3030
import org.springframework.data.mongodb.core.query.TextCriteria;
3131
import org.springframework.data.repository.query.QueryMethod;
3232
import org.springframework.data.repository.query.QueryMethodEvaluationContextProvider;
33-
import org.springframework.data.repository.query.QueryMethodValueEvaluationContextProviderFactory;
33+
import org.springframework.data.repository.query.QueryMethodValueEvaluationContextAccessor;
3434
import org.springframework.data.repository.query.RepositoryQuery;
3535
import org.springframework.data.repository.query.ResultProcessor;
3636
import org.springframework.data.repository.query.ReturnedType;
37-
import org.springframework.data.repository.query.ValueExpressionSupportHolder;
37+
import org.springframework.data.repository.query.ValueExpressionDelegate;
3838
import org.springframework.data.repository.query.parser.PartTree;
3939
import org.springframework.expression.ExpressionParser;
4040
import org.springframework.util.StringUtils;
@@ -61,29 +61,31 @@ public class PartTreeMongoQuery extends AbstractMongoQuery {
6161
* @param mongoOperations must not be {@literal null}.
6262
* @param expressionParser must not be {@literal null}.
6363
* @param evaluationContextProvider must not be {@literal null}.
64-
* @deprecated since 4.3, use the constructors accepting {@link ValueExpressionSupportHolder} instead.
64+
* @deprecated since 4.3, use the constructors accepting {@link QueryMethodValueEvaluationContextAccessor} instead.
6565
*/
6666
@Deprecated(since = "4.3")
6767
public PartTreeMongoQuery(MongoQueryMethod method, MongoOperations mongoOperations, ExpressionParser expressionParser,
6868
QueryMethodEvaluationContextProvider evaluationContextProvider) {
69-
this(method, mongoOperations,
70-
new ValueExpressionSupportHolder(
71-
new QueryMethodValueEvaluationContextProviderFactory(new StandardEnvironment(), evaluationContextProvider),
72-
ValueExpressionParser.create(() -> expressionParser)));
69+
super(method, mongoOperations, expressionParser, evaluationContextProvider);
70+
71+
this.processor = method.getResultProcessor();
72+
this.tree = new PartTree(method.getName(), processor.getReturnedType().getDomainType());
73+
this.isGeoNearQuery = method.isGeoNearQuery();
74+
this.context = mongoOperations.getConverter().getMappingContext();
7375
}
7476

7577
/**
7678
* Creates a new {@link PartTreeMongoQuery} from the given {@link QueryMethod} and {@link MongoTemplate}.
7779
*
7880
* @param method must not be {@literal null}.
7981
* @param mongoOperations must not be {@literal null}.
80-
* @param expressionSupportHolder must not be {@literal null}.
82+
* @param delegate must not be {@literal null}.
8183
* @since 4.3
8284
*/
8385
public PartTreeMongoQuery(MongoQueryMethod method, MongoOperations mongoOperations,
84-
ValueExpressionSupportHolder expressionSupportHolder) {
86+
ValueExpressionDelegate delegate) {
8587

86-
super(method, mongoOperations, expressionSupportHolder);
88+
super(method, mongoOperations, delegate);
8789

8890
this.processor = method.getResultProcessor();
8991
this.tree = new PartTree(method.getName(), processor.getReturnedType().getDomainType());

0 commit comments

Comments
 (0)