Allow configuration of QueryEngine for Map repositories.
EnableMapRepositories now accepts a QueryEngineFactory to configure the QueryEngine and allows configuring the QueryCreator. Closes: #576 Original Pull Request: #577
This commit is contained in:
committed by
Christoph Strobl
parent
22a2035634
commit
b714e5e41b
@@ -32,7 +32,7 @@ import org.springframework.lang.Nullable;
|
|||||||
* @author Christoph Strobl
|
* @author Christoph Strobl
|
||||||
* @since 3.3
|
* @since 3.3
|
||||||
*/
|
*/
|
||||||
class PredicateQueryEngine extends QueryEngine<KeyValueAdapter, Predicate<?>, Comparator<?>> {
|
public class PredicateQueryEngine extends QueryEngine<KeyValueAdapter, Predicate<?>, Comparator<?>> {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a new {@link PredicateQueryEngine}.
|
* Creates a new {@link PredicateQueryEngine}.
|
||||||
|
|||||||
@@ -0,0 +1,37 @@
|
|||||||
|
/*
|
||||||
|
* 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.keyvalue.core;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Interface for {@code QueryEngineFactory} implementations that provide a {@link QueryEngine} object as part of the
|
||||||
|
* configuration.
|
||||||
|
* <p>
|
||||||
|
* The factory is used during configuration to supply the query engine to be used. When configured, a
|
||||||
|
* {@code QueryEngineFactory} can be instantiated by accepting a {@link SortAccessor} in its constructor. Otherwise,
|
||||||
|
* implementations are expected to declare a no-args constructor.
|
||||||
|
*
|
||||||
|
* @author Mark Paluch
|
||||||
|
* @since 3.3.1
|
||||||
|
*/
|
||||||
|
public interface QueryEngineFactory {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Factory method for creating a {@link QueryEngine}.
|
||||||
|
*
|
||||||
|
* @return the query engine.
|
||||||
|
*/
|
||||||
|
QueryEngine<?, ?, ?> create();
|
||||||
|
}
|
||||||
@@ -34,9 +34,8 @@ import org.springframework.lang.Nullable;
|
|||||||
* @author Christoph Strobl
|
* @author Christoph Strobl
|
||||||
* @author Oliver Gierke
|
* @author Oliver Gierke
|
||||||
* @author Mark Paluch
|
* @author Mark Paluch
|
||||||
* @param <T>
|
|
||||||
*/
|
*/
|
||||||
class SpelQueryEngine extends QueryEngine<KeyValueAdapter, SpelCriteria, Comparator<?>> {
|
public class SpelQueryEngine extends QueryEngine<KeyValueAdapter, SpelCriteria, Comparator<?>> {
|
||||||
|
|
||||||
private static final SpelExpressionParser PARSER = new SpelExpressionParser();
|
private static final SpelExpressionParser PARSER = new SpelExpressionParser();
|
||||||
|
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ import org.springframework.beans.factory.support.BeanDefinitionBuilder;
|
|||||||
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
|
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
|
||||||
import org.springframework.beans.factory.support.RootBeanDefinition;
|
import org.springframework.beans.factory.support.RootBeanDefinition;
|
||||||
import org.springframework.core.annotation.AnnotationAttributes;
|
import org.springframework.core.annotation.AnnotationAttributes;
|
||||||
|
import org.springframework.core.annotation.MergedAnnotation;
|
||||||
import org.springframework.core.type.AnnotationMetadata;
|
import org.springframework.core.type.AnnotationMetadata;
|
||||||
import org.springframework.data.keyvalue.core.mapping.context.KeyValueMappingContext;
|
import org.springframework.data.keyvalue.core.mapping.context.KeyValueMappingContext;
|
||||||
import org.springframework.data.keyvalue.repository.KeyValueRepository;
|
import org.springframework.data.keyvalue.repository.KeyValueRepository;
|
||||||
@@ -36,7 +37,6 @@ import org.springframework.data.repository.config.RepositoryConfigurationExtensi
|
|||||||
import org.springframework.data.repository.config.RepositoryConfigurationExtensionSupport;
|
import org.springframework.data.repository.config.RepositoryConfigurationExtensionSupport;
|
||||||
import org.springframework.data.repository.config.RepositoryConfigurationSource;
|
import org.springframework.data.repository.config.RepositoryConfigurationSource;
|
||||||
import org.springframework.lang.Nullable;
|
import org.springframework.lang.Nullable;
|
||||||
import org.springframework.util.CollectionUtils;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* {@link RepositoryConfigurationExtension} for {@link KeyValueRepository}.
|
* {@link RepositoryConfigurationExtension} for {@link KeyValueRepository}.
|
||||||
@@ -90,17 +90,15 @@ public abstract class KeyValueRepositoryConfigurationExtension extends Repositor
|
|||||||
*/
|
*/
|
||||||
private static Class<?> getQueryCreatorType(AnnotationRepositoryConfigurationSource config) {
|
private static Class<?> getQueryCreatorType(AnnotationRepositoryConfigurationSource config) {
|
||||||
|
|
||||||
AnnotationMetadata metadata = config.getEnableAnnotationMetadata();
|
AnnotationMetadata amd = (AnnotationMetadata) config.getSource();
|
||||||
|
MergedAnnotation<QueryCreatorType> queryCreator = amd.getAnnotations().get(QueryCreatorType.class);
|
||||||
|
Class<?> queryCreatorType = queryCreator.isPresent() ? queryCreator.getClass("value") : Class.class;
|
||||||
|
|
||||||
Map<String, Object> queryCreatorAnnotationAttributes = metadata
|
if (queryCreatorType == Class.class) {
|
||||||
.getAnnotationAttributes(QueryCreatorType.class.getName());
|
|
||||||
|
|
||||||
if (CollectionUtils.isEmpty(queryCreatorAnnotationAttributes)) {
|
|
||||||
return SpelQueryCreator.class;
|
return SpelQueryCreator.class;
|
||||||
}
|
}
|
||||||
|
|
||||||
AnnotationAttributes queryCreatorAttributes = new AnnotationAttributes(queryCreatorAnnotationAttributes);
|
return queryCreatorType;
|
||||||
return queryCreatorAttributes.getClass("value");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -132,10 +130,10 @@ public abstract class KeyValueRepositoryConfigurationExtension extends Repositor
|
|||||||
|
|
||||||
registerIfNotAlreadyRegistered(() -> {
|
registerIfNotAlreadyRegistered(() -> {
|
||||||
|
|
||||||
RootBeanDefinition definitionefinition = new RootBeanDefinition(KeyValueMappingContext.class);
|
RootBeanDefinition mappingContext = new RootBeanDefinition(KeyValueMappingContext.class);
|
||||||
definitionefinition.setSource(configurationSource.getSource());
|
mappingContext.setSource(configurationSource.getSource());
|
||||||
|
|
||||||
return definitionefinition;
|
return mappingContext;
|
||||||
|
|
||||||
}, registry, getMappingContextBeanRef(), configurationSource);
|
}, registry, getMappingContextBeanRef(), configurationSource);
|
||||||
|
|
||||||
|
|||||||
@@ -89,7 +89,7 @@ public class KeyValuePartTreeQuery implements RepositoryQuery {
|
|||||||
QueryCreatorFactory<AbstractQueryCreator<KeyValueQuery<?>, ?>> queryCreatorFactory) {
|
QueryCreatorFactory<AbstractQueryCreator<KeyValueQuery<?>, ?>> queryCreatorFactory) {
|
||||||
|
|
||||||
Assert.notNull(queryMethod, "Query method must not be null");
|
Assert.notNull(queryMethod, "Query method must not be null");
|
||||||
Assert.notNull(evaluationContextProvider, "EvaluationContextprovider must not be null");
|
Assert.notNull(evaluationContextProvider, "EvaluationContextProvider must not be null");
|
||||||
Assert.notNull(keyValueOperations, "KeyValueOperations must not be null");
|
Assert.notNull(keyValueOperations, "KeyValueOperations must not be null");
|
||||||
Assert.notNull(queryCreatorFactory, "QueryCreatorFactory type must not be null");
|
Assert.notNull(queryCreatorFactory, "QueryCreatorFactory type must not be null");
|
||||||
|
|
||||||
|
|||||||
@@ -38,6 +38,8 @@ import org.springframework.util.ObjectUtils;
|
|||||||
import org.springframework.util.comparator.NullSafeComparator;
|
import org.springframework.util.comparator.NullSafeComparator;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* {@link AbstractQueryCreator} to create {@link Predicate}-based {@link KeyValueQuery}s.
|
||||||
|
*
|
||||||
* @author Christoph Strobl
|
* @author Christoph Strobl
|
||||||
* @since 3.3
|
* @since 3.3
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -32,7 +32,7 @@ import org.springframework.expression.spel.standard.SpelExpressionParser;
|
|||||||
import org.springframework.util.StringUtils;
|
import org.springframework.util.StringUtils;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* {@link AbstractQueryCreator} to create {@link SpelExpression} based {@link KeyValueQuery}s.
|
* {@link AbstractQueryCreator} to create {@link SpelExpression}-based {@link KeyValueQuery}s.
|
||||||
*
|
*
|
||||||
* @author Christoph Strobl
|
* @author Christoph Strobl
|
||||||
* @author Oliver Gierke
|
* @author Oliver Gierke
|
||||||
@@ -180,8 +180,8 @@ public class SpelQueryCreator extends AbstractQueryCreator<KeyValueQuery<SpelExp
|
|||||||
case NEGATING_SIMPLE_PROPERTY:
|
case NEGATING_SIMPLE_PROPERTY:
|
||||||
case EXISTS:
|
case EXISTS:
|
||||||
default:
|
default:
|
||||||
throw new InvalidDataAccessApiUsageException(String.format("Found invalid part '%s' in query",
|
throw new InvalidDataAccessApiUsageException(
|
||||||
part.getType()));
|
String.format("Found invalid part '%s' in query", part.getType()));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (partIter.hasNext()) {
|
if (partIter.hasNext()) {
|
||||||
|
|||||||
@@ -28,16 +28,18 @@ import org.springframework.beans.factory.FactoryBean;
|
|||||||
import org.springframework.context.ApplicationContext;
|
import org.springframework.context.ApplicationContext;
|
||||||
import org.springframework.context.annotation.ComponentScan.Filter;
|
import org.springframework.context.annotation.ComponentScan.Filter;
|
||||||
import org.springframework.context.annotation.Import;
|
import org.springframework.context.annotation.Import;
|
||||||
|
import org.springframework.core.annotation.AliasFor;
|
||||||
import org.springframework.data.keyvalue.core.KeyValueOperations;
|
import org.springframework.data.keyvalue.core.KeyValueOperations;
|
||||||
import org.springframework.data.keyvalue.core.KeyValueTemplate;
|
import org.springframework.data.keyvalue.core.KeyValueTemplate;
|
||||||
|
import org.springframework.data.keyvalue.core.QueryEngineFactory;
|
||||||
import org.springframework.data.keyvalue.core.SortAccessor;
|
import org.springframework.data.keyvalue.core.SortAccessor;
|
||||||
import org.springframework.data.keyvalue.repository.config.QueryCreatorType;
|
import org.springframework.data.keyvalue.repository.config.QueryCreatorType;
|
||||||
import org.springframework.data.keyvalue.repository.query.KeyValuePartTreeQuery;
|
|
||||||
import org.springframework.data.keyvalue.repository.query.PredicateQueryCreator;
|
import org.springframework.data.keyvalue.repository.query.PredicateQueryCreator;
|
||||||
import org.springframework.data.keyvalue.repository.support.KeyValueRepositoryFactoryBean;
|
import org.springframework.data.keyvalue.repository.support.KeyValueRepositoryFactoryBean;
|
||||||
import org.springframework.data.repository.config.DefaultRepositoryBaseClass;
|
import org.springframework.data.repository.config.DefaultRepositoryBaseClass;
|
||||||
import org.springframework.data.repository.query.QueryLookupStrategy;
|
import org.springframework.data.repository.query.QueryLookupStrategy;
|
||||||
import org.springframework.data.repository.query.QueryLookupStrategy.Key;
|
import org.springframework.data.repository.query.QueryLookupStrategy.Key;
|
||||||
|
import org.springframework.data.repository.query.parser.AbstractQueryCreator;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Annotation to activate Map repositories. If no base package is configured through either {@link #value()},
|
* Annotation to activate Map repositories. If no base package is configured through either {@link #value()},
|
||||||
@@ -45,13 +47,14 @@ import org.springframework.data.repository.query.QueryLookupStrategy.Key;
|
|||||||
*
|
*
|
||||||
* @author Christoph Strobl
|
* @author Christoph Strobl
|
||||||
* @author Oliver Gierke
|
* @author Oliver Gierke
|
||||||
|
* @author Mark Paluch
|
||||||
*/
|
*/
|
||||||
@Target(ElementType.TYPE)
|
@Target(ElementType.TYPE)
|
||||||
@Retention(RetentionPolicy.RUNTIME)
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
@Documented
|
@Documented
|
||||||
@Inherited
|
@Inherited
|
||||||
@Import(MapRepositoriesRegistrar.class)
|
@Import(MapRepositoriesRegistrar.class)
|
||||||
@QueryCreatorType(value = PredicateQueryCreator.class, repositoryQueryType = KeyValuePartTreeQuery.class)
|
@QueryCreatorType(PredicateQueryCreator.class)
|
||||||
public @interface EnableMapRepositories {
|
public @interface EnableMapRepositories {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -145,10 +148,29 @@ public @interface EnableMapRepositories {
|
|||||||
@SuppressWarnings("rawtypes")
|
@SuppressWarnings("rawtypes")
|
||||||
Class<? extends Map> mapType() default ConcurrentHashMap.class;
|
Class<? extends Map> mapType() default ConcurrentHashMap.class;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Configures the {@link QueryEngineFactory} to create the QueryEngine. When both, the query engine and sort accessors
|
||||||
|
* are configured, the query engine is instantiated using the configured sort accessor.
|
||||||
|
*
|
||||||
|
* @return {@link QueryEngineFactory} to configure the QueryEngine.
|
||||||
|
* @since 3.3.1
|
||||||
|
*/
|
||||||
|
Class<? extends QueryEngineFactory> queryEngineFactory() default QueryEngineFactory.class;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Configures the {@code QueryCreator} to create Part-Tree queries. The QueryCreator must create queries supported by
|
||||||
|
* the underlying {@code QueryEngine}.
|
||||||
|
*
|
||||||
|
* @return {@link AbstractQueryCreator}
|
||||||
|
* @since 3.3.1
|
||||||
|
*/
|
||||||
|
@AliasFor(annotation = QueryCreatorType.class, value = "value")
|
||||||
|
Class<? extends AbstractQueryCreator<?, ?>> queryCreator() default PredicateQueryCreator.class;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Configures the {@link SortAccessor accessor} for sorting results.
|
* Configures the {@link SortAccessor accessor} for sorting results.
|
||||||
*
|
*
|
||||||
* @return {@link SortAccessor} to indicate usage of default implementation.
|
* @return the configured {@link SortAccessor}.
|
||||||
* @since 3.1.10
|
* @since 3.1.10
|
||||||
*/
|
*/
|
||||||
Class<? extends SortAccessor> sortAccessor() default SortAccessor.class;
|
Class<? extends SortAccessor> sortAccessor() default SortAccessor.class;
|
||||||
|
|||||||
@@ -15,6 +15,7 @@
|
|||||||
*/
|
*/
|
||||||
package org.springframework.data.map.repository.config;
|
package org.springframework.data.map.repository.config;
|
||||||
|
|
||||||
|
import java.lang.reflect.Constructor;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
import org.springframework.beans.BeanUtils;
|
import org.springframework.beans.BeanUtils;
|
||||||
@@ -24,15 +25,23 @@ import org.springframework.beans.factory.support.BeanDefinitionBuilder;
|
|||||||
import org.springframework.core.type.AnnotationMetadata;
|
import org.springframework.core.type.AnnotationMetadata;
|
||||||
import org.springframework.data.config.ParsingUtils;
|
import org.springframework.data.config.ParsingUtils;
|
||||||
import org.springframework.data.keyvalue.core.KeyValueTemplate;
|
import org.springframework.data.keyvalue.core.KeyValueTemplate;
|
||||||
|
import org.springframework.data.keyvalue.core.QueryEngine;
|
||||||
|
import org.springframework.data.keyvalue.core.QueryEngineFactory;
|
||||||
import org.springframework.data.keyvalue.core.SortAccessor;
|
import org.springframework.data.keyvalue.core.SortAccessor;
|
||||||
import org.springframework.data.keyvalue.repository.config.KeyValueRepositoryConfigurationExtension;
|
import org.springframework.data.keyvalue.repository.config.KeyValueRepositoryConfigurationExtension;
|
||||||
import org.springframework.data.map.MapKeyValueAdapter;
|
import org.springframework.data.map.MapKeyValueAdapter;
|
||||||
|
import org.springframework.data.repository.config.RepositoryConfigurationExtension;
|
||||||
import org.springframework.data.repository.config.RepositoryConfigurationSource;
|
import org.springframework.data.repository.config.RepositoryConfigurationSource;
|
||||||
import org.springframework.lang.Nullable;
|
import org.springframework.lang.Nullable;
|
||||||
|
import org.springframework.util.ClassUtils;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* {@link RepositoryConfigurationExtension} for Map-based repositories.
|
||||||
|
*
|
||||||
* @author Christoph Strobl
|
* @author Christoph Strobl
|
||||||
|
* @author Mark Paluch
|
||||||
*/
|
*/
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
public class MapRepositoryConfigurationExtension extends KeyValueRepositoryConfigurationExtension {
|
public class MapRepositoryConfigurationExtension extends KeyValueRepositoryConfigurationExtension {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -58,7 +67,11 @@ public class MapRepositoryConfigurationExtension extends KeyValueRepositoryConfi
|
|||||||
adapterBuilder.addConstructorArgValue(getMapTypeToUse(configurationSource));
|
adapterBuilder.addConstructorArgValue(getMapTypeToUse(configurationSource));
|
||||||
|
|
||||||
SortAccessor<?> sortAccessor = getSortAccessor(configurationSource);
|
SortAccessor<?> sortAccessor = getSortAccessor(configurationSource);
|
||||||
if(sortAccessor != null) {
|
QueryEngine<?, ?, ?> queryEngine = getQueryEngine(sortAccessor, configurationSource);
|
||||||
|
|
||||||
|
if (queryEngine != null) {
|
||||||
|
adapterBuilder.addConstructorArgValue(queryEngine);
|
||||||
|
} else if (sortAccessor != null) {
|
||||||
adapterBuilder.addConstructorArgValue(sortAccessor);
|
adapterBuilder.addConstructorArgValue(sortAccessor);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -73,20 +86,60 @@ public class MapRepositoryConfigurationExtension extends KeyValueRepositoryConfi
|
|||||||
@SuppressWarnings({ "unchecked", "rawtypes" })
|
@SuppressWarnings({ "unchecked", "rawtypes" })
|
||||||
private static Class<? extends Map> getMapTypeToUse(RepositoryConfigurationSource source) {
|
private static Class<? extends Map> getMapTypeToUse(RepositoryConfigurationSource source) {
|
||||||
|
|
||||||
return (Class<? extends Map>) ((AnnotationMetadata) source.getSource()).getAnnotationAttributes(
|
return (Class<? extends Map>) getAnnotationAttributes(source).get("mapType");
|
||||||
EnableMapRepositories.class.getName()).get("mapType");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
private static SortAccessor<?> getSortAccessor(RepositoryConfigurationSource source) {
|
private static SortAccessor<?> getSortAccessor(RepositoryConfigurationSource source) {
|
||||||
|
|
||||||
Class<? extends SortAccessor<?>> sortAccessorType = (Class<? extends SortAccessor<?>>) ((AnnotationMetadata) source.getSource()).getAnnotationAttributes(
|
Class<? extends SortAccessor<?>> sortAccessorType = (Class<? extends SortAccessor<?>>) getAnnotationAttributes(
|
||||||
EnableMapRepositories.class.getName()).get("sortAccessor");
|
source).get("sortAccessor");
|
||||||
|
|
||||||
if(sortAccessorType != null && !sortAccessorType.isInterface()) {
|
if (sortAccessorType != null && !sortAccessorType.isInterface()) {
|
||||||
return BeanUtils.instantiateClass(sortAccessorType);
|
return BeanUtils.instantiateClass(sortAccessorType);
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
private static QueryEngine<?, ?, ?> getQueryEngine(@Nullable SortAccessor<?> sortAccessor,
|
||||||
|
RepositoryConfigurationSource source) {
|
||||||
|
|
||||||
|
Class<? extends QueryEngineFactory> queryEngineFactoryType = (Class<? extends QueryEngineFactory>) getAnnotationAttributes(
|
||||||
|
source).get("queryEngineFactory");
|
||||||
|
|
||||||
|
if (queryEngineFactoryType != null && !queryEngineFactoryType.isInterface()) {
|
||||||
|
|
||||||
|
if (sortAccessor != null) {
|
||||||
|
Constructor<? extends QueryEngineFactory> constructor = ClassUtils
|
||||||
|
.getConstructorIfAvailable(queryEngineFactoryType, SortAccessor.class);
|
||||||
|
if (constructor != null) {
|
||||||
|
return BeanUtils.instantiateClass(constructor, sortAccessor).create();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return BeanUtils.instantiateClass(queryEngineFactoryType).create();
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Map<String, Object> getAnnotationAttributes(RepositoryConfigurationSource source) {
|
||||||
|
|
||||||
|
AnnotationMetadata annotationSource = (AnnotationMetadata) source.getSource();
|
||||||
|
|
||||||
|
if (annotationSource == null) {
|
||||||
|
throw new IllegalArgumentException("AnnotationSource not available");
|
||||||
|
}
|
||||||
|
|
||||||
|
Map<String, Object> annotationAttributes = annotationSource
|
||||||
|
.getAnnotationAttributes(EnableMapRepositories.class.getName());
|
||||||
|
|
||||||
|
if (annotationAttributes == null) {
|
||||||
|
throw new IllegalStateException("No annotation attributes for @EnableMapRepositories");
|
||||||
|
}
|
||||||
|
|
||||||
|
return annotationAttributes;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,8 +19,11 @@ import static org.assertj.core.api.Assertions.*;
|
|||||||
import static org.mockito.Mockito.*;
|
import static org.mockito.Mockito.*;
|
||||||
|
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
import java.util.Comparator;
|
||||||
|
import java.util.Iterator;
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
import java.util.concurrent.ConcurrentSkipListMap;
|
import java.util.concurrent.ConcurrentSkipListMap;
|
||||||
|
import java.util.function.Predicate;
|
||||||
|
|
||||||
import org.assertj.core.api.InstanceOfAssertFactories;
|
import org.assertj.core.api.InstanceOfAssertFactories;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
@@ -32,12 +35,24 @@ import org.springframework.context.annotation.ComponentScan;
|
|||||||
import org.springframework.context.annotation.Configuration;
|
import org.springframework.context.annotation.Configuration;
|
||||||
import org.springframework.context.annotation.FilterType;
|
import org.springframework.context.annotation.FilterType;
|
||||||
import org.springframework.data.annotation.Id;
|
import org.springframework.data.annotation.Id;
|
||||||
|
import org.springframework.data.domain.Sort;
|
||||||
import org.springframework.data.keyvalue.core.KeyValueAdapter;
|
import org.springframework.data.keyvalue.core.KeyValueAdapter;
|
||||||
import org.springframework.data.keyvalue.core.KeyValueOperations;
|
import org.springframework.data.keyvalue.core.KeyValueOperations;
|
||||||
import org.springframework.data.keyvalue.core.KeyValueTemplate;
|
import org.springframework.data.keyvalue.core.KeyValueTemplate;
|
||||||
import org.springframework.data.keyvalue.core.PathSortAccessor;
|
import org.springframework.data.keyvalue.core.PathSortAccessor;
|
||||||
|
import org.springframework.data.keyvalue.core.QueryEngine;
|
||||||
|
import org.springframework.data.keyvalue.core.QueryEngineFactory;
|
||||||
|
import org.springframework.data.keyvalue.core.SortAccessor;
|
||||||
|
import org.springframework.data.keyvalue.core.SpelQueryEngine;
|
||||||
|
import org.springframework.data.keyvalue.core.query.KeyValueQuery;
|
||||||
import org.springframework.data.keyvalue.repository.KeyValueRepository;
|
import org.springframework.data.keyvalue.repository.KeyValueRepository;
|
||||||
|
import org.springframework.data.keyvalue.repository.query.PredicateQueryCreator;
|
||||||
|
import org.springframework.data.keyvalue.repository.support.KeyValueRepositoryFactoryBean;
|
||||||
import org.springframework.data.map.MapKeyValueAdapter;
|
import org.springframework.data.map.MapKeyValueAdapter;
|
||||||
|
import org.springframework.data.repository.query.ParameterAccessor;
|
||||||
|
import org.springframework.data.repository.query.parser.AbstractQueryCreator;
|
||||||
|
import org.springframework.data.repository.query.parser.Part;
|
||||||
|
import org.springframework.data.repository.query.parser.PartTree;
|
||||||
import org.springframework.test.util.ReflectionTestUtils;
|
import org.springframework.test.util.ReflectionTestUtils;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -78,7 +93,8 @@ class MapRepositoriesConfigurationExtensionIntegrationTests {
|
|||||||
|
|
||||||
PersonRepository repository = context.getBean(PersonRepository.class);
|
PersonRepository repository = context.getBean(PersonRepository.class);
|
||||||
|
|
||||||
assertThatThrownBy(() -> repository.findById("foo")).hasRootCauseInstanceOf(IllegalStateException.class).hasMessageContaining("Mock");
|
assertThatThrownBy(() -> repository.findById("foo")).hasRootCauseInstanceOf(IllegalStateException.class)
|
||||||
|
.hasMessageContaining("Mock");
|
||||||
|
|
||||||
context.close();
|
context.close();
|
||||||
}
|
}
|
||||||
@@ -101,6 +117,41 @@ class MapRepositoriesConfigurationExtensionIntegrationTests {
|
|||||||
new AnnotationConfigApplicationContext(ConfigWithCustomizedSortAccessor.class));
|
new AnnotationConfigApplicationContext(ConfigWithCustomizedSortAccessor.class));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test // GH-576
|
||||||
|
void considersQueryEngineConfiguration() {
|
||||||
|
|
||||||
|
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(ConfigWithQueryEngine.class);
|
||||||
|
|
||||||
|
KeyValueTemplate template = context.getBean(KeyValueTemplate.class);
|
||||||
|
Object adapter = ReflectionTestUtils.getField(template, "adapter");
|
||||||
|
|
||||||
|
assertThat(adapter).isInstanceOf(MapKeyValueAdapter.class);
|
||||||
|
|
||||||
|
Object engine = ReflectionTestUtils.getField(adapter, "engine");
|
||||||
|
|
||||||
|
assertThat(engine).isInstanceOf(SpelQueryEngine.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test // GH-576
|
||||||
|
void considersQueryEngineAndSortAccessorConfiguration() {
|
||||||
|
|
||||||
|
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(
|
||||||
|
ConfigWithQueryEngineAndCustomizedSortAccessor.class);
|
||||||
|
|
||||||
|
KeyValueTemplate template = context.getBean(KeyValueTemplate.class);
|
||||||
|
Object adapter = ReflectionTestUtils.getField(template, "adapter");
|
||||||
|
|
||||||
|
assertThat(adapter).isInstanceOf(MapKeyValueAdapter.class);
|
||||||
|
|
||||||
|
Object engine = ReflectionTestUtils.getField(adapter, "engine");
|
||||||
|
|
||||||
|
assertThat(engine).isInstanceOf(SpelQueryEngine.class);
|
||||||
|
Object sortAccessor = ReflectionTestUtils.getField(engine, "sortAccessor");
|
||||||
|
|
||||||
|
assertThat(sortAccessor).asInstanceOf(InstanceOfAssertFactories.OPTIONAL)
|
||||||
|
.containsInstanceOf(PathSortAccessor.class);
|
||||||
|
}
|
||||||
|
|
||||||
private static void assertKeyValueTemplateWithAdapterFor(Class<?> mapType, ApplicationContext context) {
|
private static void assertKeyValueTemplateWithAdapterFor(Class<?> mapType, ApplicationContext context) {
|
||||||
|
|
||||||
KeyValueTemplate template = context.getBean(KeyValueTemplate.class);
|
KeyValueTemplate template = context.getBean(KeyValueTemplate.class);
|
||||||
@@ -123,8 +174,32 @@ class MapRepositoriesConfigurationExtensionIntegrationTests {
|
|||||||
assertThat(sortAccessor).asInstanceOf(InstanceOfAssertFactories.OPTIONAL).containsInstanceOf(sortAccessorType);
|
assertThat(sortAccessor).asInstanceOf(InstanceOfAssertFactories.OPTIONAL).containsInstanceOf(sortAccessorType);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test // GH-576
|
||||||
|
void considersDefaultQueryCreator() {
|
||||||
|
|
||||||
|
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(Config.class);
|
||||||
|
|
||||||
|
KeyValueRepositoryFactoryBean<?, ?, ?> factoryBean = context.getBean(KeyValueRepositoryFactoryBean.class);
|
||||||
|
Object queryCreator = ReflectionTestUtils.getField(factoryBean, "queryCreator");
|
||||||
|
|
||||||
|
assertThat(queryCreator).isEqualTo(PredicateQueryCreator.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test // GH-576
|
||||||
|
void considersCustomQueryCreator() {
|
||||||
|
|
||||||
|
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(
|
||||||
|
ConfigWithCustomQueryCreator.class);
|
||||||
|
|
||||||
|
KeyValueRepositoryFactoryBean<?, ?, ?> factoryBean = context.getBean(KeyValueRepositoryFactoryBean.class);
|
||||||
|
Object queryCreator = ReflectionTestUtils.getField(factoryBean, "queryCreator");
|
||||||
|
|
||||||
|
assertThat(queryCreator).isEqualTo(MyQueryCreator.class);
|
||||||
|
}
|
||||||
|
|
||||||
@Configuration
|
@Configuration
|
||||||
@EnableMapRepositories
|
@EnableMapRepositories(considerNestedRepositories = true,
|
||||||
|
includeFilters = @ComponentScan.Filter(value = PersonRepository.class, type = FilterType.ASSIGNABLE_TYPE))
|
||||||
static class Config {}
|
static class Config {}
|
||||||
|
|
||||||
@Configuration
|
@Configuration
|
||||||
@@ -167,9 +242,74 @@ class MapRepositoriesConfigurationExtensionIntegrationTests {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@EnableMapRepositories(queryEngineFactory = JustSpelQueryEngineFactory.class)
|
||||||
|
static class ConfigWithQueryEngine {}
|
||||||
|
|
||||||
|
static class JustSpelQueryEngineFactory implements QueryEngineFactory {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public QueryEngine<?, ?, ?> create() {
|
||||||
|
return new SpelQueryEngine();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
@EnableMapRepositories(sortAccessor = PathSortAccessor.class)
|
@EnableMapRepositories(sortAccessor = PathSortAccessor.class)
|
||||||
static class ConfigWithCustomizedSortAccessor {}
|
static class ConfigWithCustomizedSortAccessor {}
|
||||||
|
|
||||||
|
@EnableMapRepositories(sortAccessor = PathSortAccessor.class, queryEngineFactory = SpelQueryEngineFactory.class)
|
||||||
|
static class ConfigWithQueryEngineAndCustomizedSortAccessor {}
|
||||||
|
|
||||||
|
@EnableMapRepositories(queryCreator = MyQueryCreator.class, considerNestedRepositories = true,
|
||||||
|
includeFilters = @ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE, classes = PersonRepository.class))
|
||||||
|
static class ConfigWithCustomQueryCreator {}
|
||||||
|
|
||||||
|
static class SpelQueryEngineFactory implements QueryEngineFactory {
|
||||||
|
|
||||||
|
private final SortAccessor<Comparator<?>> sortAccessor;
|
||||||
|
|
||||||
|
public SpelQueryEngineFactory(SortAccessor<Comparator<?>> sortAccessor) {
|
||||||
|
this.sortAccessor = sortAccessor;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public QueryEngine<?, ?, ?> create() {
|
||||||
|
return new SpelQueryEngine(sortAccessor);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
static class MyQueryCreator extends AbstractQueryCreator<KeyValueQuery<Predicate<?>>, Predicate<?>> {
|
||||||
|
|
||||||
|
public MyQueryCreator(PartTree tree) {
|
||||||
|
super(tree);
|
||||||
|
}
|
||||||
|
|
||||||
|
public MyQueryCreator(PartTree tree, ParameterAccessor parameters) {
|
||||||
|
super(tree, parameters);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Predicate<?> create(Part part, Iterator<Object> iterator) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Predicate<?> and(Part part, Predicate<?> base, Iterator<Object> iterator) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Predicate<?> or(Predicate<?> base, Predicate<?> criteria) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected KeyValueQuery<Predicate<?>> complete(Predicate<?> criteria, Sort sort) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
interface PersonRepository extends KeyValueRepository<Person, String> {
|
interface PersonRepository extends KeyValueRepository<Person, String> {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user