diff --git a/pom.xml b/pom.xml index 0ee6e9e..08145b2 100644 --- a/pom.xml +++ b/pom.xml @@ -13,7 +13,7 @@ org.springframework.data.build spring-data-parent 1.8.0.BUILD-SNAPSHOT - ../spring-data-build/parent/pom.xml + @@ -49,7 +49,7 @@ joda-time joda-time - 2.5 + ${jodatime} test 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 efa7b27..e53f4e9 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 @@ -1,5 +1,5 @@ /* - * Copyright 2014 the original author or authors. + * Copyright 2014-2015 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. @@ -20,6 +20,7 @@ import java.lang.reflect.Constructor; import org.springframework.beans.BeanUtils; import org.springframework.data.domain.PageImpl; import org.springframework.data.domain.Pageable; +import org.springframework.data.domain.Sort; import org.springframework.data.keyvalue.core.IterableConverter; import org.springframework.data.keyvalue.core.KeyValueOperations; import org.springframework.data.keyvalue.core.query.KeyValueQuery; @@ -28,10 +29,12 @@ 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.RepositoryQuery; +import org.springframework.data.repository.query.ResultProcessor; import org.springframework.data.repository.query.parser.AbstractQueryCreator; import org.springframework.data.repository.query.parser.PartTree; import org.springframework.expression.EvaluationContext; import org.springframework.expression.spel.standard.SpelExpression; +import org.springframework.util.Assert; import org.springframework.util.ClassUtils; /** @@ -50,25 +53,58 @@ public class KeyValuePartTreeQuery implements RepositoryQuery { private KeyValueQuery query; - public KeyValuePartTreeQuery(QueryMethod queryMethod, EvaluationContextProvider evalContextProvider, + /** + * 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 keyValueOperations must not be {@literal null}. + * @param queryCreator must not be {@literal null}. + */ + public KeyValuePartTreeQuery(QueryMethod queryMethod, EvaluationContextProvider evaluationContextProvider, KeyValueOperations keyValueOperations, Class> queryCreator) { + Assert.notNull(queryMethod, "Query method must not be null!"); + Assert.notNull(evaluationContextProvider, "EvaluationContextprovider must not be null!"); + Assert.notNull(keyValueOperations, "KeyValueOperations must not be null!"); + Assert.notNull(queryCreator, "QueryCreator type must not be null!"); + this.queryMethod = queryMethod; this.keyValueOperations = keyValueOperations; - this.evaluationContextProvider = evalContextProvider; + this.evaluationContextProvider = evaluationContextProvider; this.queryCreator = queryCreator; } + /* + * (non-Javadoc) + * @see org.springframework.data.repository.query.RepositoryQuery#getQueryMethod() + */ @Override public QueryMethod getQueryMethod() { return queryMethod; } - @SuppressWarnings({ "unchecked", "rawtypes" }) + /* + * (non-Javadoc) + * @see org.springframework.data.repository.query.RepositoryQuery#execute(java.lang.Object[]) + */ @Override public Object execute(Object[] parameters) { - KeyValueQuery query = prepareQuery(parameters); + ParameterAccessor accessor = new ParametersParameterAccessor(getQueryMethod().getParameters(), parameters); + KeyValueQuery query = prepareQuery(parameters, accessor); + ResultProcessor processor = queryMethod.getResultProcessor().withDynamicProjection(accessor); + + return processor.processResult(doExecute(parameters, query)); + } + + /** + * @param parameters + * @param query + */ + @SuppressWarnings({ "unchecked", "rawtypes" }) + protected Object doExecute(Object[] parameters, KeyValueQuery query) { if (queryMethod.isPageQuery() || queryMethod.isSliceQuery()) { @@ -78,8 +114,8 @@ public class KeyValuePartTreeQuery implements RepositoryQuery { Iterable result = this.keyValueOperations.find(query, queryMethod.getEntityInformation().getJavaType()); - long count = queryMethod.isSliceQuery() ? 0 : keyValueOperations.count(query, queryMethod.getEntityInformation() - .getJavaType()); + long count = queryMethod.isSliceQuery() ? 0 + : keyValueOperations.count(query, queryMethod.getEntityInformation().getJavaType()); return new PageImpl(IterableConverter.toList(result), page, count); @@ -91,16 +127,13 @@ public class KeyValuePartTreeQuery implements RepositoryQuery { Iterable result = this.keyValueOperations.find(query, queryMethod.getEntityInformation().getJavaType()); return result.iterator().hasNext() ? result.iterator().next() : null; - } throw new UnsupportedOperationException("Query method not supported."); } @SuppressWarnings({ "rawtypes", "unchecked" }) - private KeyValueQuery prepareQuery(Object[] parameters) { - - ParametersParameterAccessor accessor = new ParametersParameterAccessor(getQueryMethod().getParameters(), parameters); + private KeyValueQuery prepareQuery(Object[] parameters, ParameterAccessor accessor) { if (this.query == null) { this.query = createQuery(accessor); @@ -116,11 +149,9 @@ public class KeyValuePartTreeQuery implements RepositoryQuery { q.setRows(-1); } - if (accessor.getSort() != null) { - q.setSort(accessor.getSort()); - } else { - q.setSort(this.query.getSort()); - } + Sort sort = accessor.getSort(); + + q.setSort(sort != null ? sort : query.getSort()); if (q.getCritieria() instanceof SpelExpression) { EvaluationContext context = this.evaluationContextProvider.getEvaluationContext(getQueryMethod().getParameters(), @@ -131,7 +162,7 @@ public class KeyValuePartTreeQuery implements RepositoryQuery { return q; } - public KeyValueQuery createQuery(ParametersParameterAccessor accessor) { + public KeyValueQuery createQuery(ParameterAccessor accessor) { PartTree tree = new PartTree(getQueryMethod().getName(), getQueryMethod().getEntityInformation().getJavaType()); 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 919b50c..54c34c7 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 @@ -25,6 +25,7 @@ import org.springframework.data.keyvalue.repository.query.KeyValuePartTreeQuery; import org.springframework.data.keyvalue.repository.query.SpelQueryCreator; import org.springframework.data.mapping.PersistentEntity; import org.springframework.data.mapping.context.MappingContext; +import org.springframework.data.projection.ProjectionFactory; import org.springframework.data.querydsl.QueryDslPredicateExecutor; import org.springframework.data.repository.core.EntityInformation; import org.springframework.data.repository.core.NamedQueries; @@ -170,13 +171,15 @@ public class KeyValueRepositoryFactory extends RepositoryFactorySupport { this.queryCreator = queryCreator; } - /* + /* * (non-Javadoc) - * @see org.springframework.data.repository.query.QueryLookupStrategy#resolveQuery(java.lang.reflect.Method, org.springframework.data.repository.core.RepositoryMetadata, org.springframework.data.repository.core.NamedQueries) + * @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) */ - public RepositoryQuery resolveQuery(Method method, RepositoryMetadata metadata, NamedQueries namedQueries) { + @Override + public RepositoryQuery resolveQuery(Method method, RepositoryMetadata metadata, ProjectionFactory factory, + NamedQueries namedQueries) { - QueryMethod queryMethod = new QueryMethod(method, metadata); + QueryMethod queryMethod = new QueryMethod(method, metadata, factory); return new KeyValuePartTreeQuery(queryMethod, evaluationContextProvider, this.keyValueOperations, this.queryCreator); } diff --git a/src/test/java/org/springframework/data/keyvalue/core/SpelQueryEngineUnitTests.java b/src/test/java/org/springframework/data/keyvalue/core/SpelQueryEngineUnitTests.java index fe15fd5..a27b058 100644 --- a/src/test/java/org/springframework/data/keyvalue/core/SpelQueryEngineUnitTests.java +++ b/src/test/java/org/springframework/data/keyvalue/core/SpelQueryEngineUnitTests.java @@ -35,6 +35,7 @@ import org.mockito.runners.MockitoJUnitRunner; import org.springframework.data.annotation.Id; import org.springframework.data.keyvalue.core.query.KeyValueQuery; import org.springframework.data.keyvalue.repository.query.SpelQueryCreator; +import org.springframework.data.projection.SpelAwareProxyProjectionFactory; import org.springframework.data.repository.core.RepositoryMetadata; import org.springframework.data.repository.query.ParametersParameterAccessor; import org.springframework.data.repository.query.QueryMethod; @@ -100,11 +101,12 @@ public class SpelQueryEngineUnitTests { } Method method = PersonRepository.class.getMethod(methodName, types.toArray(new Class[types.size()])); - RepositoryMetadata metadata = mock(RepositoryMetadata.class); + doReturn(method.getReturnType()).when(metadata).getReturnedDomainClass(method); + PartTree partTree = new PartTree(method.getName(), method.getReturnType()); - SpelQueryCreator creator = new SpelQueryCreator(partTree, - new ParametersParameterAccessor(new QueryMethod(method, metadata).getParameters(), args)); + SpelQueryCreator creator = new SpelQueryCreator(partTree, new ParametersParameterAccessor( + new QueryMethod(method, metadata, new SpelAwareProxyProjectionFactory()).getParameters(), args)); KeyValueQuery query = creator.createQuery(); query.getCritieria().setEvaluationContext(new StandardEvaluationContext(args)); diff --git a/src/test/java/org/springframework/data/keyvalue/repository/query/SpelQueryCreatorUnitTests.java b/src/test/java/org/springframework/data/keyvalue/repository/query/SpelQueryCreatorUnitTests.java index 20c0a61..e4a4b38 100644 --- a/src/test/java/org/springframework/data/keyvalue/repository/query/SpelQueryCreatorUnitTests.java +++ b/src/test/java/org/springframework/data/keyvalue/repository/query/SpelQueryCreatorUnitTests.java @@ -17,6 +17,7 @@ package org.springframework.data.keyvalue.repository.query; import static org.hamcrest.core.Is.*; import static org.junit.Assert.*; +import static org.mockito.Mockito.*; import java.lang.reflect.Method; import java.util.Date; @@ -30,6 +31,7 @@ import org.mockito.runners.MockitoJUnitRunner; import org.springframework.dao.InvalidDataAccessApiUsageException; import org.springframework.data.annotation.Id; import org.springframework.data.keyvalue.core.query.KeyValueQuery; +import org.springframework.data.projection.SpelAwareProxyProjectionFactory; import org.springframework.data.repository.core.RepositoryMetadata; import org.springframework.data.repository.query.ParametersParameterAccessor; import org.springframework.data.repository.query.QueryMethod; @@ -238,7 +240,7 @@ public class SpelQueryCreatorUnitTests { * @see DATACMNS-525 */ @Test - public void greaterThanEaualsReturnsTrueForEqualValues() throws Exception { + public void greaterThanEqualsReturnsTrueForEqualValues() throws Exception { assertThat(evaluate("findByAgeGreaterThanEqual", BRAN.age).against(BRAN), is(true)); } @@ -394,10 +396,11 @@ public class SpelQueryCreatorUnitTests { } Method method = PersonRepository.class.getMethod(methodName, argTypes); + doReturn(Person.class).when(metadataMock).getReturnedDomainClass(method); PartTree partTree = new PartTree(method.getName(), method.getReturnType()); - SpelQueryCreator creator = new SpelQueryCreator(partTree, new ParametersParameterAccessor(new QueryMethod(method, - metadataMock).getParameters(), args)); + SpelQueryCreator creator = new SpelQueryCreator(partTree, new ParametersParameterAccessor( + new QueryMethod(method, metadataMock, new SpelAwareProxyProjectionFactory()).getParameters(), args)); KeyValueQuery q = creator.createQuery(); q.getCritieria().setEvaluationContext(new StandardEvaluationContext(args)); diff --git a/src/test/java/org/springframework/data/map/AbstractRepositoryUnitTests.java b/src/test/java/org/springframework/data/map/AbstractRepositoryUnitTests.java index 3e2d7de..1a9e869 100644 --- a/src/test/java/org/springframework/data/map/AbstractRepositoryUnitTests.java +++ b/src/test/java/org/springframework/data/map/AbstractRepositoryUnitTests.java @@ -155,6 +155,34 @@ public abstract class AbstractRepositoryUnitTests result = repository.findByAgeGreaterThan(0, new Sort("firstname")); + + assertThat(result, hasSize(3)); + assertThat(result.get(0).getFirstname(), is(CERSEI.getFirstname())); + } + + /** + * @see DATAKV-121 + */ + @Test + public void projectsResultToDynamicInterface() { + + repository.save(LENNISTERS); + + List result = repository.findByAgeGreaterThan(0, new Sort("firstname"), PersonSummary.class); + + assertThat(result, hasSize(3)); + assertThat(result.get(0).getFirstname(), is(CERSEI.getFirstname())); + } + protected abstract T getRepository(KeyValueRepositoryFactory factory); public static interface PersonRepository extends CrudRepository, KeyValueRepository { @@ -173,6 +201,13 @@ public abstract class AbstractRepositoryUnitTests findByAgeGreaterThanOrderByAgeAscFirstnameDesc(int age); + List findByAgeGreaterThan(int age, Sort sort); + + List findByAgeGreaterThan(int age, Sort sort, Class projectionType); } + interface PersonSummary { + + String getFirstname(); + } }