diff --git a/src/main/java/org/springframework/data/keyvalue/repository/query/CachingKeyValuePartTreeQuery.java b/src/main/java/org/springframework/data/keyvalue/repository/query/CachingKeyValuePartTreeQuery.java index 94ec0f01..dc1a2de3 100644 --- a/src/main/java/org/springframework/data/keyvalue/repository/query/CachingKeyValuePartTreeQuery.java +++ b/src/main/java/org/springframework/data/keyvalue/repository/query/CachingKeyValuePartTreeQuery.java @@ -18,7 +18,7 @@ import org.springframework.data.keyvalue.core.KeyValueOperations; import org.springframework.data.keyvalue.core.query.KeyValueQuery; import org.springframework.data.repository.query.QueryMethod; -import org.springframework.data.repository.query.QueryMethodEvaluationContextProvider; +import org.springframework.data.repository.query.ValueExpressionDelegate; import org.springframework.data.repository.query.parser.AbstractQueryCreator; import org.springframework.data.repository.query.parser.PartTree; import org.springframework.lang.Nullable; @@ -36,9 +36,9 @@ public class CachingKeyValuePartTreeQuery extends KeyValuePartTreeQuery { private @Nullable KeyValueQuery cachedQuery; public CachingKeyValuePartTreeQuery(QueryMethod queryMethod, - QueryMethodEvaluationContextProvider evaluationContextProvider, KeyValueOperations keyValueOperations, + ValueExpressionDelegate valueExpressionDelegate, KeyValueOperations keyValueOperations, Class> queryCreator) { - super(queryMethod, evaluationContextProvider, keyValueOperations, queryCreator); + super(queryMethod, valueExpressionDelegate, keyValueOperations, queryCreator); } protected KeyValueQuery prepareQuery(Object[] parameters) { diff --git a/src/main/java/org/springframework/data/keyvalue/repository/query/KeyValuePartTreeQuery.java b/src/main/java/org/springframework/data/keyvalue/repository/query/KeyValuePartTreeQuery.java index 1305a97d..82dad31b 100644 --- a/src/main/java/org/springframework/data/keyvalue/repository/query/KeyValuePartTreeQuery.java +++ b/src/main/java/org/springframework/data/keyvalue/repository/query/KeyValuePartTreeQuery.java @@ -21,6 +21,8 @@ import org.springframework.data.domain.PageImpl; import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Sort; +import org.springframework.data.expression.ValueEvaluationContext; +import org.springframework.data.expression.ValueEvaluationContextProvider; import org.springframework.data.keyvalue.core.IterableConverter; import org.springframework.data.keyvalue.core.KeyValueOperations; import org.springframework.data.keyvalue.core.SpelCriteria; @@ -28,14 +30,13 @@ import org.springframework.data.repository.query.ParameterAccessor; import org.springframework.data.repository.query.ParametersParameterAccessor; import org.springframework.data.repository.query.QueryMethod; -import org.springframework.data.repository.query.QueryMethodEvaluationContextProvider; import org.springframework.data.repository.query.RepositoryQuery; import org.springframework.data.repository.query.ResultProcessor; +import org.springframework.data.repository.query.ValueExpressionDelegate; import org.springframework.data.repository.query.parser.AbstractQueryCreator; import org.springframework.data.repository.query.parser.PartTree; import org.springframework.data.spel.EvaluationContextProvider; import org.springframework.data.util.Lazy; -import org.springframework.expression.EvaluationContext; import org.springframework.expression.spel.standard.SpelExpression; import org.springframework.lang.Nullable; import org.springframework.util.Assert; @@ -54,22 +55,23 @@ public class KeyValuePartTreeQuery implements RepositoryQuery { private final Lazy partTree; private final QueryMethod queryMethod; private final KeyValueOperations keyValueOperations; - private final QueryMethodEvaluationContextProvider evaluationContextProvider; + private final ValueExpressionDelegate valueExpressionDelegate; private final QueryCreatorFactory, ?>> queryCreatorFactory; + private final ValueEvaluationContextProvider evaluationContextProvider; /** * Creates a new {@link KeyValuePartTreeQuery} for the given {@link QueryMethod}, {@link EvaluationContextProvider}, * {@link KeyValueOperations} and query creator type. * * @param queryMethod must not be {@literal null}. - * @param evaluationContextProvider must not be {@literal null}. + * @param valueExpressionDelegate must not be {@literal null}. * @param keyValueOperations must not be {@literal null}. * @param queryCreator must not be {@literal null}. */ - public KeyValuePartTreeQuery(QueryMethod queryMethod, QueryMethodEvaluationContextProvider evaluationContextProvider, + public KeyValuePartTreeQuery(QueryMethod queryMethod, ValueExpressionDelegate valueExpressionDelegate, KeyValueOperations keyValueOperations, Class> queryCreator) { - this(queryMethod, evaluationContextProvider, keyValueOperations, + this(queryMethod, valueExpressionDelegate, keyValueOperations, new ConstructorCachingQueryCreatorFactory(queryCreator)); } @@ -79,17 +81,17 @@ public KeyValuePartTreeQuery(QueryMethod queryMethod, QueryMethodEvaluationConte * in charge of altering the query. * * @param queryMethod must not be {@literal null}. - * @param evaluationContextProvider must not be {@literal null}. + * @param valueExpressionDelegate must not be {@literal null}. * @param keyValueOperations must not be {@literal null}. * @param queryCreatorFactory must not be {@literal null}. * @since 2.0 */ - public KeyValuePartTreeQuery(QueryMethod queryMethod, QueryMethodEvaluationContextProvider evaluationContextProvider, + public KeyValuePartTreeQuery(QueryMethod queryMethod, ValueExpressionDelegate valueExpressionDelegate, KeyValueOperations keyValueOperations, QueryCreatorFactory, ?>> queryCreatorFactory) { Assert.notNull(queryMethod, "Query method must not be null"); - Assert.notNull(evaluationContextProvider, "EvaluationContextProvider must not be null"); + Assert.notNull(valueExpressionDelegate, "ValueExpressionDelegate must not be null"); Assert.notNull(keyValueOperations, "KeyValueOperations must not be null"); Assert.notNull(queryCreatorFactory, "QueryCreatorFactory type must not be null"); @@ -97,8 +99,9 @@ public KeyValuePartTreeQuery(QueryMethod queryMethod, QueryMethodEvaluationConte .of(() -> new PartTree(queryMethod.getName(), queryMethod.getEntityInformation().getJavaType())); this.queryMethod = queryMethod; this.keyValueOperations = keyValueOperations; - this.evaluationContextProvider = evaluationContextProvider; + this.valueExpressionDelegate = valueExpressionDelegate; this.queryCreatorFactory = queryCreatorFactory; + this.evaluationContextProvider = valueExpressionDelegate.createValueContextProvider(queryMethod.getParameters()); } @Override @@ -160,9 +163,8 @@ protected KeyValueQuery prepareQuery(KeyValueQuery instance, Object[] para if (criteria instanceof SpelCriteria || criteria instanceof SpelExpression) { SpelExpression spelExpression = getSpelExpression(criteria); - EvaluationContext context = this.evaluationContextProvider.getEvaluationContext(getQueryMethod().getParameters(), - parameters); - criteria = new SpelCriteria(spelExpression, context); + ValueEvaluationContext context = this.evaluationContextProvider.getEvaluationContext(parameters); + criteria = new SpelCriteria(spelExpression, context.getRequiredEvaluationContext()); } KeyValueQuery query = new KeyValueQuery(criteria); diff --git a/src/main/java/org/springframework/data/keyvalue/repository/support/KeyValueRepositoryFactory.java b/src/main/java/org/springframework/data/keyvalue/repository/support/KeyValueRepositoryFactory.java index 0fdc176d..6dbbdb32 100644 --- a/src/main/java/org/springframework/data/keyvalue/repository/support/KeyValueRepositoryFactory.java +++ b/src/main/java/org/springframework/data/keyvalue/repository/support/KeyValueRepositoryFactory.java @@ -41,8 +41,8 @@ import org.springframework.data.repository.query.QueryLookupStrategy; import org.springframework.data.repository.query.QueryLookupStrategy.Key; import org.springframework.data.repository.query.QueryMethod; -import org.springframework.data.repository.query.QueryMethodEvaluationContextProvider; import org.springframework.data.repository.query.RepositoryQuery; +import org.springframework.data.repository.query.ValueExpressionDelegate; import org.springframework.data.repository.query.parser.AbstractQueryCreator; import org.springframework.lang.Nullable; import org.springframework.util.Assert; @@ -175,8 +175,8 @@ private static boolean isQueryDslRepository(Class repositoryInterface) { @Override protected Optional getQueryLookupStrategy(@Nullable Key key, - QueryMethodEvaluationContextProvider evaluationContextProvider) { - return Optional.of(new KeyValueQueryLookupStrategy(key, evaluationContextProvider, this.keyValueOperations, + ValueExpressionDelegate valueExpressionDelegate) { + return Optional.of(new KeyValueQueryLookupStrategy(key, valueExpressionDelegate, this.keyValueOperations, this.queryCreator, this.repositoryQueryType)); } @@ -186,7 +186,7 @@ protected Optional getQueryLookupStrategy(@Nullable Key key */ private static class KeyValueQueryLookupStrategy implements QueryLookupStrategy { - private final QueryMethodEvaluationContextProvider evaluationContextProvider; + private final ValueExpressionDelegate valueExpressionDelegate; private final KeyValueOperations keyValueOperations; private final Class> queryCreator; @@ -194,22 +194,21 @@ private static class KeyValueQueryLookupStrategy implements QueryLookupStrategy /** * @param key - * @param evaluationContextProvider + * @param valueExpressionDelegate * @param keyValueOperations * @param queryCreator * @since 1.1 */ - public KeyValueQueryLookupStrategy(@Nullable Key key, - QueryMethodEvaluationContextProvider evaluationContextProvider, KeyValueOperations keyValueOperations, - Class> queryCreator, + public KeyValueQueryLookupStrategy(@Nullable Key key, ValueExpressionDelegate valueExpressionDelegate, + KeyValueOperations keyValueOperations, Class> queryCreator, Class repositoryQueryType) { - Assert.notNull(evaluationContextProvider, "EvaluationContextProvider must not be null"); + Assert.notNull(valueExpressionDelegate, "ValueExpressionDelegate must not be null"); Assert.notNull(keyValueOperations, "KeyValueOperations must not be null"); Assert.notNull(queryCreator, "Query creator type must not be null"); Assert.notNull(repositoryQueryType, "RepositoryQueryType type must not be null"); - this.evaluationContextProvider = evaluationContextProvider; + this.valueExpressionDelegate = valueExpressionDelegate; this.keyValueOperations = keyValueOperations; this.queryCreator = queryCreator; this.repositoryQueryType = repositoryQueryType; @@ -223,15 +222,15 @@ public RepositoryQuery resolveQuery(Method method, RepositoryMetadata metadata, QueryMethod queryMethod = new QueryMethod(method, metadata, factory); Constructor constructor = (Constructor) ClassUtils - .getConstructorIfAvailable(this.repositoryQueryType, QueryMethod.class, - QueryMethodEvaluationContextProvider.class, KeyValueOperations.class, Class.class); + .getConstructorIfAvailable(this.repositoryQueryType, QueryMethod.class, ValueExpressionDelegate.class, + KeyValueOperations.class, Class.class); Assert.state(constructor != null, String.format( "Constructor %s(QueryMethod, EvaluationContextProvider, KeyValueOperations, Class) not available", ClassUtils.getShortName(this.repositoryQueryType))); - return BeanUtils.instantiateClass(constructor, queryMethod, evaluationContextProvider, this.keyValueOperations, + return BeanUtils.instantiateClass(constructor, queryMethod, valueExpressionDelegate, this.keyValueOperations, this.queryCreator); } } diff --git a/src/test/java/org/springframework/data/keyvalue/repository/query/AbstractQueryCreatorTestBase.java b/src/test/java/org/springframework/data/keyvalue/repository/query/AbstractQueryCreatorTestBase.java index 1d11e468..8e6f2fb1 100644 --- a/src/test/java/org/springframework/data/keyvalue/repository/query/AbstractQueryCreatorTestBase.java +++ b/src/test/java/org/springframework/data/keyvalue/repository/query/AbstractQueryCreatorTestBase.java @@ -28,6 +28,9 @@ import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; +import org.mockito.junit.jupiter.MockitoSettings; +import org.mockito.quality.Strictness; + import org.springframework.dao.InvalidDataAccessApiUsageException; import org.springframework.data.annotation.Id; import org.springframework.data.keyvalue.core.query.KeyValueQuery; @@ -45,6 +48,7 @@ * @author Christoph Strobl */ @ExtendWith(MockitoExtension.class) +@MockitoSettings(strictness = Strictness.LENIENT) public abstract class AbstractQueryCreatorTestBase, ?>, CRITERIA> { static final DateTimeFormatter FORMATTER = DateTimeFormatter.ISO_ZONED_DATE_TIME; diff --git a/src/test/java/org/springframework/data/keyvalue/repository/query/CachingKeyValuePartTreeQueryUnitTests.java b/src/test/java/org/springframework/data/keyvalue/repository/query/CachingKeyValuePartTreeQueryUnitTests.java index 23bbc165..dbd5617d 100644 --- a/src/test/java/org/springframework/data/keyvalue/repository/query/CachingKeyValuePartTreeQueryUnitTests.java +++ b/src/test/java/org/springframework/data/keyvalue/repository/query/CachingKeyValuePartTreeQueryUnitTests.java @@ -33,7 +33,7 @@ import org.springframework.data.projection.ProjectionFactory; import org.springframework.data.repository.core.RepositoryMetadata; import org.springframework.data.repository.query.QueryMethod; -import org.springframework.data.repository.query.QueryMethodEvaluationContextProvider; +import org.springframework.data.repository.query.ValueExpressionDelegate; import org.springframework.data.util.TypeInformation; /** @@ -64,8 +64,8 @@ void cachedSpelExpressionShouldBeReusedWithNewContext() throws NoSuchMethodExcep QueryMethod qm = new QueryMethod(Repo.class.getMethod("findByFirstname", String.class), metadataMock, projectionFactoryMock); - KeyValuePartTreeQuery query = new CachingKeyValuePartTreeQuery(qm, QueryMethodEvaluationContextProvider.DEFAULT, - kvOpsMock, SpelQueryCreator.class); + KeyValuePartTreeQuery query = new CachingKeyValuePartTreeQuery(qm, ValueExpressionDelegate.create(), kvOpsMock, + SpelQueryCreator.class); Object[] args = new Object[] { "foo" }; diff --git a/src/test/java/org/springframework/data/keyvalue/repository/query/KeyValuePartTreeQueryUnitTests.java b/src/test/java/org/springframework/data/keyvalue/repository/query/KeyValuePartTreeQueryUnitTests.java index b4f85d55..d505a627 100644 --- a/src/test/java/org/springframework/data/keyvalue/repository/query/KeyValuePartTreeQueryUnitTests.java +++ b/src/test/java/org/springframework/data/keyvalue/repository/query/KeyValuePartTreeQueryUnitTests.java @@ -35,7 +35,7 @@ import org.springframework.data.projection.ProjectionFactory; import org.springframework.data.repository.core.RepositoryMetadata; import org.springframework.data.repository.query.QueryMethod; -import org.springframework.data.repository.query.QueryMethodEvaluationContextProvider; +import org.springframework.data.repository.query.ValueExpressionDelegate; import org.springframework.data.util.TypeInformation; /** @@ -61,7 +61,7 @@ void spelExpressionAndContextShouldNotBeReused() throws NoSuchMethodException, S QueryMethod qm = new QueryMethod(Repo.class.getMethod("findByFirstname", String.class), metadataMock, projectionFactoryMock); - KeyValuePartTreeQuery query = new KeyValuePartTreeQuery(qm, QueryMethodEvaluationContextProvider.DEFAULT, kvOpsMock, + KeyValuePartTreeQuery query = new KeyValuePartTreeQuery(qm, ValueExpressionDelegate.create(), kvOpsMock, SpelQueryCreator.class); Object[] args = new Object[] { "foo" }; @@ -84,8 +84,8 @@ void shouldApplyPageableParameterToCollectionQuery() throws SecurityException, N QueryMethod qm = new QueryMethod(Repo.class.getMethod("findBy", Pageable.class), metadataMock, projectionFactoryMock); - KeyValuePartTreeQuery partTreeQuery = new KeyValuePartTreeQuery(qm, QueryMethodEvaluationContextProvider.DEFAULT, - kvOpsMock, SpelQueryCreator.class); + KeyValuePartTreeQuery partTreeQuery = new KeyValuePartTreeQuery(qm, ValueExpressionDelegate.create(), kvOpsMock, + SpelQueryCreator.class); KeyValueQuery query = partTreeQuery.prepareQuery(new Object[] { PageRequest.of(2, 3) }); @@ -102,11 +102,11 @@ void shouldAllowProjectionQueries() throws SecurityException, NoSuchMethodExcept when(metadataMock.getReturnType(any(Method.class))).thenReturn((TypeInformation) TypeInformation.of(List.class)); when(metadataMock.getReturnedDomainClass(any(Method.class))).thenReturn((Class) Person.class); - QueryMethod qm = new QueryMethod(Repo.class.getMethod("findProjectionByFirstname",String.class), metadataMock, + QueryMethod qm = new QueryMethod(Repo.class.getMethod("findProjectionByFirstname", String.class), metadataMock, projectionFactoryMock); - KeyValuePartTreeQuery partTreeQuery = new KeyValuePartTreeQuery(qm, QueryMethodEvaluationContextProvider.DEFAULT, - kvOpsMock, SpelQueryCreator.class); + KeyValuePartTreeQuery partTreeQuery = new KeyValuePartTreeQuery(qm, ValueExpressionDelegate.create(), kvOpsMock, + SpelQueryCreator.class); KeyValueQuery query = partTreeQuery.prepareQuery(new Object[] { "firstname" }); partTreeQuery.doExecute(new Object[] { "firstname" }, query); @@ -119,14 +119,13 @@ void shouldAllowProjectionQueries() throws SecurityException, NoSuchMethodExcept void shouldApplyDerivedMaxResultsToQuery() throws SecurityException, NoSuchMethodException { when(metadataMock.getDomainType()).thenReturn((Class) Person.class); - when(metadataMock.getDomainTypeInformation()).thenReturn((TypeInformation) TypeInformation.of(Person.class)); when(metadataMock.getReturnType(any(Method.class))).thenReturn((TypeInformation) TypeInformation.of(List.class)); when(metadataMock.getReturnedDomainClass(any(Method.class))).thenReturn((Class) Person.class); QueryMethod qm = new QueryMethod(Repo.class.getMethod("findTop3By"), metadataMock, projectionFactoryMock); - KeyValuePartTreeQuery partTreeQuery = new KeyValuePartTreeQuery(qm, QueryMethodEvaluationContextProvider.DEFAULT, - kvOpsMock, SpelQueryCreator.class); + KeyValuePartTreeQuery partTreeQuery = new KeyValuePartTreeQuery(qm, ValueExpressionDelegate.create(), kvOpsMock, + SpelQueryCreator.class); KeyValueQuery query = partTreeQuery.prepareQuery(new Object[] {}); @@ -145,8 +144,8 @@ void shouldApplyDerivedMaxResultsToQueryWithParameters() throws SecurityExceptio QueryMethod qm = new QueryMethod(Repo.class.getMethod("findTop3ByFirstname", String.class), metadataMock, projectionFactoryMock); - KeyValuePartTreeQuery partTreeQuery = new KeyValuePartTreeQuery(qm, QueryMethodEvaluationContextProvider.DEFAULT, - kvOpsMock, SpelQueryCreator.class); + KeyValuePartTreeQuery partTreeQuery = new KeyValuePartTreeQuery(qm, ValueExpressionDelegate.create(), kvOpsMock, + SpelQueryCreator.class); KeyValueQuery query = partTreeQuery.prepareQuery(new Object[] { "firstname" }); @@ -167,8 +166,8 @@ void shouldUseCountForExists() throws NoSuchMethodException { QueryMethod qm = new QueryMethod(Repo.class.getMethod("existsByFirstname", String.class), metadataMock, projectionFactoryMock); - KeyValuePartTreeQuery partTreeQuery = new KeyValuePartTreeQuery(qm, QueryMethodEvaluationContextProvider.DEFAULT, - kvOpsMock, SpelQueryCreator.class); + KeyValuePartTreeQuery partTreeQuery = new KeyValuePartTreeQuery(qm, ValueExpressionDelegate.create(), kvOpsMock, + SpelQueryCreator.class); KeyValueQuery query = partTreeQuery.prepareQuery(new Object[] { "firstname" }); partTreeQuery.doExecute(new Object[] { "firstname" }, query);