From 551b27deb266a2644fa86d9e272622784d534e66 Mon Sep 17 00:00:00 2001 From: Christoph Strobl Date: Thu, 3 Sep 2015 09:02:40 +0200 Subject: [PATCH] DATAKV-112 - Make caching repository queries more explicit. We introduced an explicit CachingKeyValuePartTreeQuery and allow setting the gernal repository query stategy via QueryCreatorType. By default queries will not be cached. Original pull request: #18. --- ...ValueRepositoryConfigurationExtension.java | 31 +++++++++-- .../repository/config/QueryCreatorType.java | 13 ++++- .../query/CachingKeyValuePartTreeQuery.java | 53 +++++++++++++++++++ .../query/KeyValuePartTreeQuery.java | 40 +++++++------- .../support/KeyValueRepositoryFactory.java | 48 ++++++++++++++++- .../KeyValueRepositoryFactoryBean.java | 30 ++++++++--- .../config/EnableMapRepositories.java | 5 +- ...eyValueRepositoryFactoryBeanUnitTests.java | 17 ++++++ 8 files changed, 202 insertions(+), 35 deletions(-) create mode 100644 src/main/java/org/springframework/data/keyvalue/repository/query/CachingKeyValuePartTreeQuery.java diff --git a/src/main/java/org/springframework/data/keyvalue/repository/config/KeyValueRepositoryConfigurationExtension.java b/src/main/java/org/springframework/data/keyvalue/repository/config/KeyValueRepositoryConfigurationExtension.java index 30b148c..4700636 100644 --- a/src/main/java/org/springframework/data/keyvalue/repository/config/KeyValueRepositoryConfigurationExtension.java +++ b/src/main/java/org/springframework/data/keyvalue/repository/config/KeyValueRepositoryConfigurationExtension.java @@ -1,5 +1,5 @@ /* - * Copyright 2014 the original author or authors. + * Copyright 2014-2016 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. @@ -27,6 +27,7 @@ import org.springframework.core.annotation.AnnotationAttributes; import org.springframework.core.type.AnnotationMetadata; import org.springframework.data.keyvalue.core.mapping.context.KeyValueMappingContext; import org.springframework.data.keyvalue.repository.KeyValueRepository; +import org.springframework.data.keyvalue.repository.query.KeyValuePartTreeQuery; import org.springframework.data.keyvalue.repository.query.SpelQueryCreator; import org.springframework.data.keyvalue.repository.support.KeyValueRepositoryFactoryBean; import org.springframework.data.repository.config.AnnotationRepositoryConfigurationSource; @@ -92,6 +93,7 @@ public abstract class KeyValueRepositoryConfigurationExtension extends Repositor builder.addPropertyReference("keyValueOperations", attributes.getString(KEY_VALUE_TEMPLATE_BEAN_REF_ATTRIBUTE)); builder.addPropertyValue("queryCreator", getQueryCreatorType(config)); + builder.addPropertyValue("queryType", getQueryType(config)); builder.addPropertyReference("mappingContext", MAPPING_CONTEXT_BEAN_NAME); } @@ -106,16 +108,37 @@ public abstract class KeyValueRepositoryConfigurationExtension extends Repositor AnnotationMetadata metadata = config.getEnableAnnotationMetadata(); - Map queryCreatorFoo = metadata.getAnnotationAttributes(QueryCreatorType.class.getName()); + Map queryCreatorAnnotationAttributes = metadata.getAnnotationAttributes(QueryCreatorType.class.getName()); - if (queryCreatorFoo == null) { + if (queryCreatorAnnotationAttributes == null) { return SpelQueryCreator.class; } - AnnotationAttributes queryCreatorAttributes = new AnnotationAttributes(queryCreatorFoo); + AnnotationAttributes queryCreatorAttributes = new AnnotationAttributes(queryCreatorAnnotationAttributes); return queryCreatorAttributes.getClass("value"); } + /** + * Detects the query creator type to be used for the factory to set. Will lookup a {@link QueryCreatorType} annotation + * on the {@code @Enable}-annotation or use {@link SpelQueryCreator} if not found. + * + * @param config + * @return + */ + private static Class getQueryType(AnnotationRepositoryConfigurationSource config) { + + AnnotationMetadata metadata = config.getEnableAnnotationMetadata(); + + Map queryCreatorAnnotationAttributes = metadata.getAnnotationAttributes(QueryCreatorType.class.getName()); + + if (queryCreatorAnnotationAttributes == null) { + return KeyValuePartTreeQuery.class; + } + + AnnotationAttributes queryCreatorAttributes = new AnnotationAttributes(queryCreatorAnnotationAttributes); + return queryCreatorAttributes.getClass("repositoryQueryType"); + } + /* * (non-Javadoc) * @see org.springframework.data.repository.config.RepositoryConfigurationExtensionSupport#registerBeansForRoot(org.springframework.beans.factory.support.BeanDefinitionRegistry, org.springframework.data.repository.config.RepositoryConfigurationSource) diff --git a/src/main/java/org/springframework/data/keyvalue/repository/config/QueryCreatorType.java b/src/main/java/org/springframework/data/keyvalue/repository/config/QueryCreatorType.java index 44b05a2..c464c9d 100644 --- a/src/main/java/org/springframework/data/keyvalue/repository/config/QueryCreatorType.java +++ b/src/main/java/org/springframework/data/keyvalue/repository/config/QueryCreatorType.java @@ -1,5 +1,5 @@ /* - * Copyright 2014 the original author or authors. + * Copyright 2014-2016 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. @@ -21,12 +21,15 @@ import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; +import org.springframework.data.keyvalue.repository.query.KeyValuePartTreeQuery; +import org.springframework.data.repository.query.RepositoryQuery; import org.springframework.data.repository.query.parser.AbstractQueryCreator; /** * Annotation to customize the query creator type to be used for a specific store. * * @author Oliver Gierke + * @author Christoph Strobl */ @Documented @Retention(RetentionPolicy.RUNTIME) @@ -34,4 +37,12 @@ import org.springframework.data.repository.query.parser.AbstractQueryCreator; public @interface QueryCreatorType { Class> value(); + + /** + * The {@link RepositoryQuery} type to be created by the {@link QueryCreatorType#value()}. + * + * @return + * @since 1.1 + */ + Class repositoryQueryType() default KeyValuePartTreeQuery.class; } 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 new file mode 100644 index 0000000..66fd098 --- /dev/null +++ b/src/main/java/org/springframework/data/keyvalue/repository/query/CachingKeyValuePartTreeQuery.java @@ -0,0 +1,53 @@ +/* + * Copyright 2016 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 + * + * http://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.repository.query; + +import org.springframework.data.keyvalue.core.KeyValueOperations; +import org.springframework.data.keyvalue.core.query.KeyValueQuery; +import org.springframework.data.repository.query.EvaluationContextProvider; +import org.springframework.data.repository.query.QueryMethod; +import org.springframework.data.repository.query.parser.AbstractQueryCreator; +import org.springframework.data.repository.query.parser.PartTree; + +/** + * {@link KeyValuePartTreeQuery} implementation deriving queries from {@link PartTree} using a predefined + * {@link AbstractQueryCreator} that caches the once created query. + * + * @author Christoph Strobl + * @since 1.1 + */ +public class CachingKeyValuePartTreeQuery extends KeyValuePartTreeQuery { + + private KeyValueQuery cachedQuery; + + public CachingKeyValuePartTreeQuery(QueryMethod queryMethod, EvaluationContextProvider evaluationContextProvider, + KeyValueOperations keyValueOperations, Class> queryCreator) { + super(queryMethod, evaluationContextProvider, keyValueOperations, queryCreator); + } + + /* + * (non-Javadoc) + * @see org.springframework.data.keyvalue.repository.query.KeyValuePartTreeQuery#prepareQuery(java.lang.Object[]) + */ + protected KeyValueQuery prepareQuery(Object[] parameters) { + + if (cachedQuery == null) { + cachedQuery = super.prepareQuery(parameters); + } + + return prepareQuery(cachedQuery, 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 0f92054..8c5e0b9 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 @@ -52,8 +52,6 @@ public class KeyValuePartTreeQuery implements RepositoryQuery { private final KeyValueOperations keyValueOperations; private final Class> queryCreator; - private KeyValueQuery query; - /** * Creates a new {@link KeyValuePartTreeQuery} for the given {@link QueryMethod}, {@link EvaluationContextProvider}, * {@link KeyValueOperations} and query creator type. @@ -77,15 +75,6 @@ public class KeyValuePartTreeQuery implements RepositoryQuery { this.queryCreator = queryCreator; } - /* - * (non-Javadoc) - * @see org.springframework.data.repository.query.RepositoryQuery#getQueryMethod() - */ - @Override - public QueryMethod getQueryMethod() { - return queryMethod; - } - /* * (non-Javadoc) * @see org.springframework.data.repository.query.RepositoryQuery#execute(java.lang.Object[]) @@ -133,23 +122,25 @@ public class KeyValuePartTreeQuery implements RepositoryQuery { throw new UnsupportedOperationException("Query method not supported."); } - @SuppressWarnings({ "rawtypes", "unchecked" }) protected KeyValueQuery prepareQuery(Object[] parameters) { + return prepareQuery(createQuery(new ParametersParameterAccessor(getQueryMethod().getParameters(), parameters)), + parameters); + } + + @SuppressWarnings({ "rawtypes", "unchecked" }) + protected KeyValueQuery prepareQuery(KeyValueQuery instance, Object[] parameters) { + ParametersParameterAccessor accessor = new ParametersParameterAccessor(getQueryMethod().getParameters(), parameters); - if (this.query == null) { - this.query = createQuery(accessor); - } + KeyValueQuery query = new KeyValueQuery(instance.getCritieria()); - KeyValueQuery query = new KeyValueQuery(this.query.getCritieria()); - - if (this.query.getCritieria() instanceof SpelExpression) { + if (instance.getCritieria() instanceof SpelExpression) { EvaluationContext context = this.evaluationContextProvider.getEvaluationContext(getQueryMethod().getParameters(), parameters); - query = new KeyValueQuery(new SpelCriteria((SpelExpression) this.query.getCritieria(), context)); + query = new KeyValueQuery(new SpelCriteria((SpelExpression) instance.getCritieria(), context)); } Pageable pageable = accessor.getPageable(); @@ -157,7 +148,7 @@ public class KeyValuePartTreeQuery implements RepositoryQuery { query.setOffset(pageable == null ? -1 : pageable.getOffset()); query.setRows(pageable == null ? -1 : pageable.getPageSize()); - query.setSort(sort == null ? this.query.getSort() : sort); + query.setSort(sort == null ? instance.getSort() : sort); return query; } @@ -170,4 +161,13 @@ public class KeyValuePartTreeQuery implements RepositoryQuery { .getConstructorIfAvailable(queryCreator, PartTree.class, ParameterAccessor.class); return (KeyValueQuery) BeanUtils.instantiateClass(constructor, tree, accessor).createQuery(); } + + /* + * (non-Javadoc) + * @see org.springframework.data.repository.query.RepositoryQuery#getQueryMethod() + */ + @Override + public QueryMethod getQueryMethod() { + return queryMethod; + } } 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 54c34c7..b2c31ab 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 @@ -18,8 +18,10 @@ package org.springframework.data.keyvalue.repository.support; import static org.springframework.data.querydsl.QueryDslUtils.*; import java.io.Serializable; +import java.lang.reflect.Constructor; import java.lang.reflect.Method; +import org.springframework.beans.BeanUtils; import org.springframework.data.keyvalue.core.KeyValueOperations; import org.springframework.data.keyvalue.repository.query.KeyValuePartTreeQuery; import org.springframework.data.keyvalue.repository.query.SpelQueryCreator; @@ -40,6 +42,7 @@ import org.springframework.data.repository.query.QueryMethod; import org.springframework.data.repository.query.RepositoryQuery; import org.springframework.data.repository.query.parser.AbstractQueryCreator; import org.springframework.util.Assert; +import org.springframework.util.ClassUtils; /** * {@link RepositoryFactorySupport} specific of handing @@ -55,6 +58,7 @@ public class KeyValueRepositoryFactory extends RepositoryFactorySupport { private final KeyValueOperations keyValueOperations; private final MappingContext context; private final Class> queryCreator; + private final Class repositoryQueryType; /** * Creates a new {@link KeyValueRepositoryFactory} for the given {@link KeyValueOperations}. @@ -75,12 +79,29 @@ public class KeyValueRepositoryFactory extends RepositoryFactorySupport { public KeyValueRepositoryFactory(KeyValueOperations keyValueOperations, Class> queryCreator) { + this(keyValueOperations, queryCreator, KeyValuePartTreeQuery.class); + } + + /** + * Creates a new {@link KeyValueRepositoryFactory} for the given {@link KeyValueOperations} and + * {@link AbstractQueryCreator}-type. + * + * @param keyValueOperations must not be {@literal null}. + * @param queryCreator must not be {@literal null}. + * @param repositoryQueryType must not be {@literal null}. + * @since 1.1 + */ + public KeyValueRepositoryFactory(KeyValueOperations keyValueOperations, + Class> queryCreator, Class repositoryQueryType) { + 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.queryCreator = queryCreator; this.keyValueOperations = keyValueOperations; this.context = keyValueOperations.getMappingContext(); + this.repositoryQueryType = repositoryQueryType; } /* @@ -134,7 +155,8 @@ public class KeyValueRepositoryFactory extends RepositoryFactorySupport { */ @Override protected QueryLookupStrategy getQueryLookupStrategy(Key key, EvaluationContextProvider evaluationContextProvider) { - return new KeyValueQueryLookupStrategy(key, evaluationContextProvider, this.keyValueOperations, this.queryCreator); + return new KeyValueQueryLookupStrategy(key, evaluationContextProvider, this.keyValueOperations, this.queryCreator, + this.repositoryQueryType); } /** @@ -147,6 +169,7 @@ public class KeyValueRepositoryFactory extends RepositoryFactorySupport { private KeyValueOperations keyValueOperations; private Class> queryCreator; + private Class repositoryQueryType; /** * Creates a new {@link KeyValueQueryLookupStrategy} for the given {@link Key}, {@link EvaluationContextProvider}, @@ -161,14 +184,29 @@ public class KeyValueRepositoryFactory extends RepositoryFactorySupport { */ public KeyValueQueryLookupStrategy(Key key, EvaluationContextProvider evaluationContextProvider, KeyValueOperations keyValueOperations, Class> queryCreator) { + this(key, evaluationContextProvider, keyValueOperations, queryCreator, KeyValuePartTreeQuery.class); + } + + /** + * @param key + * @param evaluationContextProvider + * @param keyValueOperations + * @param queryCreator + * @since 1.1 + */ + public KeyValueQueryLookupStrategy(Key key, EvaluationContextProvider evaluationContextProvider, + KeyValueOperations keyValueOperations, Class> queryCreator, + Class repositoryQueryType) { Assert.notNull(evaluationContextProvider, "EvaluationContextProvider 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.keyValueOperations = keyValueOperations; this.queryCreator = queryCreator; + this.repositoryQueryType = repositoryQueryType; } /* @@ -176,11 +214,17 @@ public class KeyValueRepositoryFactory extends RepositoryFactorySupport { * @see org.springframework.data.repository.query.QueryLookupStrategy#resolveQuery(java.lang.reflect.Method, org.springframework.data.repository.core.RepositoryMetadata, org.springframework.data.projection.ProjectionFactory, org.springframework.data.repository.core.NamedQueries) */ @Override + @SuppressWarnings("unchecked") public RepositoryQuery resolveQuery(Method method, RepositoryMetadata metadata, ProjectionFactory factory, NamedQueries namedQueries) { QueryMethod queryMethod = new QueryMethod(method, metadata, factory); - return new KeyValuePartTreeQuery(queryMethod, evaluationContextProvider, this.keyValueOperations, + + Constructor constructor = (Constructor) ClassUtils + .getConstructorIfAvailable(this.repositoryQueryType, QueryMethod.class, EvaluationContextProvider.class, + KeyValueOperations.class, Class.class); + + return BeanUtils.instantiateClass(constructor, queryMethod, evaluationContextProvider, this.keyValueOperations, this.queryCreator); } } diff --git a/src/main/java/org/springframework/data/keyvalue/repository/support/KeyValueRepositoryFactoryBean.java b/src/main/java/org/springframework/data/keyvalue/repository/support/KeyValueRepositoryFactoryBean.java index 897cb7b..5c53a84 100644 --- a/src/main/java/org/springframework/data/keyvalue/repository/support/KeyValueRepositoryFactoryBean.java +++ b/src/main/java/org/springframework/data/keyvalue/repository/support/KeyValueRepositoryFactoryBean.java @@ -24,6 +24,7 @@ import org.springframework.data.mapping.context.MappingContext; import org.springframework.data.repository.Repository; import org.springframework.data.repository.core.support.RepositoryFactoryBeanSupport; import org.springframework.data.repository.core.support.RepositoryFactorySupport; +import org.springframework.data.repository.query.RepositoryQuery; import org.springframework.data.repository.query.parser.AbstractQueryCreator; import org.springframework.util.Assert; @@ -33,11 +34,12 @@ import org.springframework.util.Assert; * @author Christoph Strobl * @author Oliver Gierke */ -public class KeyValueRepositoryFactoryBean, S, ID extends Serializable> extends - RepositoryFactoryBeanSupport { +public class KeyValueRepositoryFactoryBean, S, ID extends Serializable> + extends RepositoryFactoryBeanSupport { private KeyValueOperations operations; private Class> queryCreator; + private Class repositoryQueryType; /** * Configures the {@link KeyValueOperations} to be used for the repositories. @@ -72,27 +74,42 @@ public class KeyValueRepositoryFactoryBean, S, ID ex this.queryCreator = queryCreator; } + /** + * Configures the {@link RepositoryQuery} type to be created. + * + * @param repositoryQueryType must not be {@literal null}. + * @since 1.1 + */ + public void setQueryType(Class repositoryQueryType) { + + Assert.notNull(queryCreator, "Query creator type must not be null!"); + + this.repositoryQueryType = repositoryQueryType; + + } + /* * (non-Javadoc) * @see org.springframework.data.repository.core.support.RepositoryFactoryBeanSupport#createRepositoryFactory() */ @Override protected final RepositoryFactorySupport createRepositoryFactory() { - return createRepositoryFactory(operations, queryCreator); + return createRepositoryFactory(operations, queryCreator, repositoryQueryType); } /** * Create the repository factory to be used to create repositories. - * + * * @param operations will never be {@literal null}. * @param queryCreator will never be {@literal null}. + * @param repositoryQueryType will never be {@literal null}. * @return must not be {@literal null}. * @since 1.1 */ protected KeyValueRepositoryFactory createRepositoryFactory(KeyValueOperations operations, - Class> queryCreator) { + Class> queryCreator, Class repositoryQueryType) { - return new KeyValueRepositoryFactory(operations, queryCreator); + return new KeyValueRepositoryFactory(operations, queryCreator, repositoryQueryType); } /* @@ -104,6 +121,7 @@ public class KeyValueRepositoryFactoryBean, S, ID ex Assert.notNull(operations, "KeyValueOperations must not be null!"); Assert.notNull(queryCreator, "Query creator type must not be null!"); + Assert.notNull(repositoryQueryType, "RepositoryQueryType type type must not be null!"); super.afterPropertiesSet(); } diff --git a/src/main/java/org/springframework/data/map/repository/config/EnableMapRepositories.java b/src/main/java/org/springframework/data/map/repository/config/EnableMapRepositories.java index 93072a2..f8db684 100644 --- a/src/main/java/org/springframework/data/map/repository/config/EnableMapRepositories.java +++ b/src/main/java/org/springframework/data/map/repository/config/EnableMapRepositories.java @@ -1,5 +1,5 @@ /* - * Copyright 2014 the original author or authors. + * Copyright 2014-2016 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. @@ -31,6 +31,7 @@ import org.springframework.context.annotation.Import; import org.springframework.data.keyvalue.core.KeyValueOperations; import org.springframework.data.keyvalue.core.KeyValueTemplate; import org.springframework.data.keyvalue.repository.config.QueryCreatorType; +import org.springframework.data.keyvalue.repository.query.CachingKeyValuePartTreeQuery; import org.springframework.data.keyvalue.repository.query.SpelQueryCreator; import org.springframework.data.keyvalue.repository.support.KeyValueRepositoryFactoryBean; import org.springframework.data.repository.config.DefaultRepositoryBaseClass; @@ -49,7 +50,7 @@ import org.springframework.data.repository.query.QueryLookupStrategy.Key; @Documented @Inherited @Import(MapRepositoriesRegistrar.class) -@QueryCreatorType(SpelQueryCreator.class) +@QueryCreatorType(value = SpelQueryCreator.class, repositoryQueryType = CachingKeyValuePartTreeQuery.class) public @interface EnableMapRepositories { /** diff --git a/src/test/java/org/springframework/data/keyvalue/repository/support/KeyValueRepositoryFactoryBeanUnitTests.java b/src/test/java/org/springframework/data/keyvalue/repository/support/KeyValueRepositoryFactoryBeanUnitTests.java index 0f2d6aa..2d0ea25 100644 --- a/src/test/java/org/springframework/data/keyvalue/repository/support/KeyValueRepositoryFactoryBeanUnitTests.java +++ b/src/test/java/org/springframework/data/keyvalue/repository/support/KeyValueRepositoryFactoryBeanUnitTests.java @@ -26,7 +26,9 @@ import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExpectedException; import org.springframework.data.keyvalue.core.KeyValueOperations; +import org.springframework.data.keyvalue.repository.query.KeyValuePartTreeQuery; import org.springframework.data.repository.Repository; +import org.springframework.data.repository.query.RepositoryQuery; import org.springframework.data.repository.query.parser.AbstractQueryCreator; /** @@ -95,6 +97,7 @@ public class KeyValueRepositoryFactoryBeanUnitTests { /** * @see DATAKV-123 + * @see DATAKV-112 */ @Test @SuppressWarnings("unchecked") @@ -102,9 +105,12 @@ public class KeyValueRepositoryFactoryBeanUnitTests { Class> creatorType = (Class>) mock( AbstractQueryCreator.class).getClass(); + Class queryType = (Class) mock(KeyValuePartTreeQuery.class) + .getClass(); factoryBean.setQueryCreator(creatorType); factoryBean.setKeyValueOperations(mock(KeyValueOperations.class)); + factoryBean.setQueryType(queryType); exception.expect(IllegalArgumentException.class); exception.expectMessage("Repository interface"); @@ -121,10 +127,21 @@ public class KeyValueRepositoryFactoryBeanUnitTests { Class> creatorType = (Class>) mock( AbstractQueryCreator.class).getClass(); + Class queryType = (Class) mock(KeyValuePartTreeQuery.class) + .getClass(); factoryBean.setQueryCreator(creatorType); factoryBean.setKeyValueOperations(mock(KeyValueOperations.class)); + factoryBean.setQueryType(queryType); assertThat(factoryBean.createRepositoryFactory(), is(notNullValue())); } + + /** + * @see DATAKV-112 + */ + @Test(expected = IllegalArgumentException.class) + public void rejectsNullQueryType() { + factoryBean.setQueryType(null); + } }