diff --git a/pom.xml b/pom.xml index 70ed6253..68cf2af0 100644 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ org.springframework.data spring-data-keyvalue - 1.1.0.BUILD-SNAPSHOT + 1.1.0.DATAKV-112-SNAPSHOT Spring Data KeyValue diff --git a/src/main/java/org/springframework/data/keyvalue/core/AbstractKeyValueAdapter.java b/src/main/java/org/springframework/data/keyvalue/core/AbstractKeyValueAdapter.java index 2a11a0ab..e76164d5 100644 --- a/src/main/java/org/springframework/data/keyvalue/core/AbstractKeyValueAdapter.java +++ b/src/main/java/org/springframework/data/keyvalue/core/AbstractKeyValueAdapter.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. @@ -57,6 +57,33 @@ protected AbstractKeyValueAdapter(QueryEngine e return engine; } + /* + * (non-Javadoc) + * @see org.springframework.data.keyvalue.core.KeyValueAdapter#get(java.io.Serializable, java.io.Serializable, java.lang.Class) + */ + @Override + public T get(Serializable id, Serializable keyspace, Class type) { + return (T) get(id, keyspace); + } + + /* + * (non-Javadoc) + * @see org.springframework.data.keyvalue.core.KeyValueAdapter#get(java.io.Serializable, java.io.Serializable, java.lang.Class) + */ + @Override + public T delete(Serializable id, Serializable keyspace, Class type) { + return (T) delete(id, keyspace); + } + + /* + * (non-Javadoc) + * @see org.springframework.data.keyvalue.core.KeyValueAdapter#get(java.io.Serializable, java.io.Serializable, java.lang.Class) + */ + @Override + public Iterable find(KeyValueQuery query, Serializable keyspace, Class type) { + return (Iterable) engine.execute(query, keyspace, type); + } + /* * (non-Javadoc) * @see org.springframework.data.keyvalue.core.KeyValueAdapter#find(org.springframework.data.keyvalue.core.query.KeyValueQuery, java.io.Serializable) diff --git a/src/main/java/org/springframework/data/keyvalue/core/KeyValueAdapter.java b/src/main/java/org/springframework/data/keyvalue/core/KeyValueAdapter.java index 1f47f180..a3bc9347 100644 --- a/src/main/java/org/springframework/data/keyvalue/core/KeyValueAdapter.java +++ b/src/main/java/org/springframework/data/keyvalue/core/KeyValueAdapter.java @@ -1,5 +1,5 @@ /* - * Copyright 2014-2015 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. @@ -58,6 +58,15 @@ public interface KeyValueAdapter extends DisposableBean { */ Object get(Serializable id, Serializable keyspace); + /** + * @param id + * @param keyspace + * @param type + * @return + * @since 1.1 + */ + T get(Serializable id, Serializable keyspace, Class type); + /** * Delete and return the obect with given type and id. * @@ -67,6 +76,15 @@ public interface KeyValueAdapter extends DisposableBean { */ Object delete(Serializable id, Serializable keyspace); + /** + * @param id + * @param keyspace + * @param type + * @return + * @since 1.1 + */ + T delete(Serializable id, Serializable keyspace, Class type); + /** * Get all elements for given keyspace. * @@ -76,7 +94,7 @@ public interface KeyValueAdapter extends DisposableBean { Iterable getAllOf(Serializable keyspace); /** - * Returns a {@link KeyValueIterator} that iterates over all entries. + * Returns a {@link CloseableIterator} that iterates over all entries. * * @param keyspace * @return @@ -104,6 +122,15 @@ public interface KeyValueAdapter extends DisposableBean { */ Iterable find(KeyValueQuery query, Serializable keyspace); + /** + * @param query + * @param keyspace + * @param type + * @return + * @since 1.1 + */ + Iterable find(KeyValueQuery query, Serializable keyspace, Class type); + /** * Count number of objects within {@literal keyspace}. * diff --git a/src/main/java/org/springframework/data/keyvalue/core/KeyValueTemplate.java b/src/main/java/org/springframework/data/keyvalue/core/KeyValueTemplate.java index 8d73945f..452fbc69 100644 --- a/src/main/java/org/springframework/data/keyvalue/core/KeyValueTemplate.java +++ b/src/main/java/org/springframework/data/keyvalue/core/KeyValueTemplate.java @@ -271,7 +271,7 @@ public T findById(final Serializable id, final Class type) { @Override public T doInKeyValue(KeyValueAdapter adapter) { - Object result = adapter.get(id, keyspace); + Object result = adapter.get(id, keyspace, type); if (result == null || typeCheck(type, result)) { return (T) result; @@ -342,10 +342,9 @@ public T delete(final Serializable id, final Class type) { T result = execute(new KeyValueCallback() { - @SuppressWarnings("unchecked") @Override public T doInKeyValue(KeyValueAdapter adapter) { - return (T) adapter.delete(id, keyspace); + return (T) adapter.delete(id, keyspace, type); } }); @@ -394,7 +393,7 @@ public Iterable find(final KeyValueQuery query, final Class type) { @Override public Iterable doInKeyValue(KeyValueAdapter adapter) { - Iterable result = adapter.find(query, resolveKeySpace(type)); + Iterable result = adapter.find(query, resolveKeySpace(type), type); if (result == null) { return Collections.emptySet(); } diff --git a/src/main/java/org/springframework/data/keyvalue/core/QueryEngine.java b/src/main/java/org/springframework/data/keyvalue/core/QueryEngine.java index 16c26a8f..ab1dde67 100644 --- a/src/main/java/org/springframework/data/keyvalue/core/QueryEngine.java +++ b/src/main/java/org/springframework/data/keyvalue/core/QueryEngine.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. @@ -56,6 +56,21 @@ public Collection execute(KeyValueQuery query, Serializable keyspace) { return execute(criteria, sort, query.getOffset(), query.getRows(), keyspace); } + /** + * Extract query attributes and delegate to concrete execution. + * + * @param query + * @param keyspace + * @return + */ + public Collection execute(KeyValueQuery query, Serializable keyspace, Class type) { + + CRITERIA criteria = this.criteriaAccessor != null ? this.criteriaAccessor.resolve(query) : null; + SORT sort = this.sortAccessor != null ? this.sortAccessor.resolve(query) : null; + + return execute(criteria, sort, query.getOffset(), query.getRows(), keyspace, type); + } + /** * Extract query attributes and delegate to concrete execution. * @@ -79,6 +94,21 @@ public long count(KeyValueQuery query, Serializable keyspace) { */ public abstract Collection execute(CRITERIA criteria, SORT sort, int offset, int rows, Serializable keyspace); + /** + * @param criteria + * @param sort + * @param offset + * @param rows + * @param keyspace + * @param type + * @return + * @since 1.1 + */ + public Collection execute(CRITERIA criteria, SORT sort, int offset, int rows, Serializable keyspace, + Class type) { + return (Collection) execute(criteria, sort, offset, rows, keyspace); + } + /** * @param criteria * @param keyspace 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 30b148ca..4700636e 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.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 void postProcess(BeanDefinitionBuilder builder, AnnotationRepositoryConfi 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 @@ private static Class getQueryCreatorType(AnnotationRepositoryConfigurationSou 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 44b05a27..c464c9dc 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.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 @@ 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 00000000..66fd0981 --- /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 0f920540..8c5e0b9c 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 KeyValuePartTreeQuery(QueryMethod queryMethod, EvaluationContextProvider 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 @@ protected Object doExecute(Object[] parameters, KeyValueQuery query) { throw new UnsupportedOperationException("Query method not supported."); } - @SuppressWarnings({ "rawtypes", "unchecked" }) protected KeyValueQuery prepareQuery(Object[] parameters) { - ParametersParameterAccessor accessor = new ParametersParameterAccessor(getQueryMethod().getParameters(), + return prepareQuery(createQuery(new ParametersParameterAccessor(getQueryMethod().getParameters(), parameters)), parameters); + } - if (this.query == null) { - this.query = createQuery(accessor); - } + @SuppressWarnings({ "rawtypes", "unchecked" }) + protected KeyValueQuery prepareQuery(KeyValueQuery instance, Object[] parameters) { + + ParametersParameterAccessor accessor = new ParametersParameterAccessor(getQueryMethod().getParameters(), + parameters); - KeyValueQuery query = new KeyValueQuery(this.query.getCritieria()); + KeyValueQuery query = new KeyValueQuery(instance.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 @@ protected KeyValueQuery prepareQuery(Object[] parameters) { 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 KeyValueQuery createQuery(ParameterAccessor accessor) { .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 54c34c7a..c76f0074 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 @@ -1,5 +1,5 @@ /* - * Copyright 2014-2015 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. @@ -18,8 +18,10 @@ 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.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 KeyValueRepositoryFactory(KeyValueOperations keyValueOperations) { 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 @@ private static boolean isQueryDslRepository(Class repositoryInterface) { */ @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 @@ private static class KeyValueQueryLookupStrategy implements QueryLookupStrategy 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 @@ private static class KeyValueQueryLookupStrategy implements QueryLookupStrategy */ 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 KeyValueQueryLookupStrategy(Key key, EvaluationContextProvider evaluation * @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 897cb7be..5c53a84f 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.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 @@ * @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 void setQueryCreator(Class> queryCre 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 void afterPropertiesSet() { 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 93072a2c..f8db684a 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.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 @@ @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/core/KeyValueTemplateUnitTests.java b/src/test/java/org/springframework/data/keyvalue/core/KeyValueTemplateUnitTests.java index 59ee40b7..9c58d595 100644 --- a/src/test/java/org/springframework/data/keyvalue/core/KeyValueTemplateUnitTests.java +++ b/src/test/java/org/springframework/data/keyvalue/core/KeyValueTemplateUnitTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2014-2015 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. @@ -212,7 +212,7 @@ public void findByIdShouldReturnObjectWithMatchingIdAndType() { template.findById("1", Foo.class); - verify(adapterMock, times(1)).get("1", Foo.class.getName()); + verify(adapterMock, times(1)).get("1", Foo.class.getName(), Foo.class); } /** @@ -250,7 +250,7 @@ public void findShouldCallFindOnAdapterToResolveMatching() { template.find(STRING_QUERY, Foo.class); - verify(adapterMock, times(1)).find(STRING_QUERY, Foo.class.getName()); + verify(adapterMock, times(1)).find(STRING_QUERY, Foo.class.getName(), Foo.class); } /** @@ -264,7 +264,7 @@ public void findInRangeShouldRespectOffset() { template.findInRange(1, 5, Foo.class); - verify(adapterMock, times(1)).find(captor.capture(), eq(Foo.class.getName())); + verify(adapterMock, times(1)).find(captor.capture(), eq(Foo.class.getName()), eq(Foo.class)); assertThat(captor.getValue().getOffset(), is(1)); assertThat(captor.getValue().getRows(), is(5)); assertThat(captor.getValue().getCritieria(), nullValue()); @@ -327,7 +327,7 @@ public void deleteShouldRemoveObjectCorrectly() { template.delete("1", Foo.class); - verify(adapterMock, times(1)).delete("1", Foo.class.getName()); + verify(adapterMock, times(1)).delete("1", Foo.class.getName(), Foo.class); } /** @@ -341,7 +341,7 @@ public void deleteRemovesObjectUsingExtractedId() { template.delete(source); - verify(adapterMock, times(1)).delete("some-id", ClassWithStringId.class.getName()); + verify(adapterMock, times(1)).delete("some-id", ClassWithStringId.class.getName(), ClassWithStringId.class); } /** @@ -616,7 +616,7 @@ public void shouldPublishBeforeDeleteEventCorrectly() { public void shouldPublishAfterDeleteEventCorrectly() { setEventsToPublish(AfterDeleteEvent.class); - when(adapterMock.delete(eq("1"), eq(FOO_ONE.getClass().getName()))).thenReturn(FOO_ONE); + when(adapterMock.delete(eq("1"), eq(FOO_ONE.getClass().getName()), eq(Foo.class))).thenReturn(FOO_ONE); template.delete("1", FOO_ONE.getClass()); @@ -663,7 +663,7 @@ public void shouldPublishAfterGetEventCorrectly() { setEventsToPublish(AfterGetEvent.class); - when(adapterMock.get(eq("1"), eq(FOO_ONE.getClass().getName()))).thenReturn(FOO_ONE); + when(adapterMock.get(eq("1"), eq(FOO_ONE.getClass().getName()), eq(Foo.class))).thenReturn(FOO_ONE); template.findById("1", FOO_ONE.getClass()); 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 0f2d6aad..2d0ea25d 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.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 void rejectsInstanceWithoutQueryCreator() { /** * @see DATAKV-123 + * @see DATAKV-112 */ @Test @SuppressWarnings("unchecked") @@ -102,9 +105,12 @@ public void initializesConfiguredFactory() { 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 void createsRepositoryFactory() { 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); + } }