From 1935d35b5f84a080d139ca3575292ade4d6d2f06 Mon Sep 17 00:00:00 2001 From: Oliver Gierke Date: Mon, 6 Aug 2012 15:29:04 +0200 Subject: [PATCH] SGF-113 - Added support to return single entities from query methods. If a query method returns a single entity it is now correctly unwrapped from the result collection returned by the query. No entities being found will result in null returned, more than one entity being found results in an IncorrectResultSizeDataAccessException to align with the other repository implementations. --- .../StringBasedGemfireRepositoryQuery.java | 32 ++++++++++++++-- .../repository/sample/PersonRepository.java | 2 + ...fireRepositoryFactoryIntegrationTests.java | 38 ++++++++++++++++++- ...fireRepositoryFactoryIntegrationTests.java | 2 +- 4 files changed, 67 insertions(+), 7 deletions(-) diff --git a/src/main/java/org/springframework/data/gemfire/repository/query/StringBasedGemfireRepositoryQuery.java b/src/main/java/org/springframework/data/gemfire/repository/query/StringBasedGemfireRepositoryQuery.java index 3dff3708..6818818b 100644 --- a/src/main/java/org/springframework/data/gemfire/repository/query/StringBasedGemfireRepositoryQuery.java +++ b/src/main/java/org/springframework/data/gemfire/repository/query/StringBasedGemfireRepositoryQuery.java @@ -19,6 +19,7 @@ import java.util.Collection; import java.util.Collections; import java.util.Iterator; +import org.springframework.dao.IncorrectResultSizeDataAccessException; import org.springframework.data.gemfire.GemfireTemplate; import org.springframework.data.repository.query.ParametersParameterAccessor; import org.springframework.util.Assert; @@ -34,6 +35,8 @@ import com.gemstone.gemfire.cache.query.internal.ResultsBag; */ public class StringBasedGemfireRepositoryQuery extends GemfireRepositoryQuery { + private static final String INVALID_EXECUTION = "Paging and modifying queries are not supported!"; + private final QueryString query; private final GemfireQueryMethod method; private final GemfireTemplate template; @@ -67,6 +70,10 @@ public class StringBasedGemfireRepositoryQuery extends GemfireRepositoryQuery { this.query = new QueryString(StringUtils.hasText(query) ? query : method.getAnnotatedQuery()); this.method = method; this.template = template; + + if (method.isPageQuery() || method.isModifyingQuery()) { + throw new IllegalStateException(INVALID_EXECUTION); + } } /* @@ -83,7 +90,24 @@ public class StringBasedGemfireRepositoryQuery extends GemfireRepositoryQuery { while (indexes.hasNext()) { query = query.bindIn(toCollection(accessor.getBindableValue(indexes.next() - 1))); } - return toCollection(template.find(query.toString(), parameters)); + + Collection result = toCollection(template.find(query.toString(), parameters)); + + if (method.isCollectionQuery()) { + return result; + } else if (method.isQueryForEntity()) { + + if (result.isEmpty()) { + return null; + } else if (result.size() == 1) { + return result.iterator().next(); + } else { + throw new IncorrectResultSizeDataAccessException(1, result.size()); + } + + } else { + throw new IllegalStateException(INVALID_EXECUTION); + } } /** @@ -94,15 +118,15 @@ public class StringBasedGemfireRepositoryQuery extends GemfireRepositoryQuery { * @return */ private Collection toCollection(Object source) { + if (source instanceof ResultsBag) { - ResultsBag bag = (ResultsBag)source; + ResultsBag bag = (ResultsBag) source; return bag.asList(); } - + if (source instanceof Collection) { return (Collection) source; } - return source.getClass().isArray() ? CollectionUtils.arrayToList(source) : Collections.singleton(source); } diff --git a/src/test/java/org/springframework/data/gemfire/repository/sample/PersonRepository.java b/src/test/java/org/springframework/data/gemfire/repository/sample/PersonRepository.java index d7ccd565..4c7bb0bb 100644 --- a/src/test/java/org/springframework/data/gemfire/repository/sample/PersonRepository.java +++ b/src/test/java/org/springframework/data/gemfire/repository/sample/PersonRepository.java @@ -42,4 +42,6 @@ public interface PersonRepository extends CrudRepository { Collection findByFirstnameAndLastname(String firstname, String lastname); Collection findByFirstnameOrLastname(String firstname, String lastname); + + Person findByLastname(String lastname); } diff --git a/src/test/java/org/springframework/data/gemfire/repository/support/AbstractGemfireRepositoryFactoryIntegrationTests.java b/src/test/java/org/springframework/data/gemfire/repository/support/AbstractGemfireRepositoryFactoryIntegrationTests.java index 4fba9928..17381dc6 100644 --- a/src/test/java/org/springframework/data/gemfire/repository/support/AbstractGemfireRepositoryFactoryIntegrationTests.java +++ b/src/test/java/org/springframework/data/gemfire/repository/support/AbstractGemfireRepositoryFactoryIntegrationTests.java @@ -26,6 +26,7 @@ import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.dao.IncorrectResultSizeDataAccessException; import org.springframework.data.gemfire.GemfireTemplate; import org.springframework.data.gemfire.mapping.GemfireMappingContext; import org.springframework.data.gemfire.mapping.Regions; @@ -46,7 +47,7 @@ public abstract class AbstractGemfireRepositoryFactoryIntegrationTests { @Autowired List> regions; - Person dave, carter, boyd, stefan, leroi, jeff; + Person dave, carter, boyd, stefan, leroi, jeff, oliverAugust; PersonRepository repository; @Before @@ -58,6 +59,7 @@ public abstract class AbstractGemfireRepositoryFactoryIntegrationTests { stefan = new Person(4L, "Stefan", "Lessard"); leroi = new Person(5L, "Leroi", "Moore"); jeff = new Person(6L, "Jeff", "Coffin"); + oliverAugust = new Person(7L, "Oliver August", "Matthews"); GemfireMappingContext context = new GemfireMappingContext(); @@ -70,6 +72,7 @@ public abstract class AbstractGemfireRepositoryFactoryIntegrationTests { template.put(stefan.id, stefan); template.put(leroi.id, leroi); template.put(jeff.id, jeff); + template.put(oliverAugust.id, oliverAugust); repository = getRepository(regions); } @@ -105,7 +108,7 @@ public abstract class AbstractGemfireRepositoryFactoryIntegrationTests { @Test public void executesDerivedQueryWithOrCorrectly() { - assertResultsFound(repository.findByFirstnameOrLastname("Carter", "Matthews"), carter, dave); + assertResultsFound(repository.findByFirstnameOrLastname("Carter", "Matthews"), carter, dave, oliverAugust); } /** @@ -119,6 +122,37 @@ public abstract class AbstractGemfireRepositoryFactoryIntegrationTests { assertResultsFound(repository.findAll()); } + /** + * @see SGF-113 + */ + @Test + public void findsPersonByLastname() { + assertThat(repository.findByLastname("Beauford"), is(carter)); + } + + /** + * @see SGF-113 + */ + @Test + public void returnsNullForEmptyResultForSingleEntityQuery() { + assertThat(repository.findByLastname("Foo"), is(nullValue())); + } + + /** + * @see SGF-113 + */ + @Test + public void throwsExceptionForMoreThanOneResultForSingleEntityQuery() { + + try { + repository.findByLastname("Matthews"); + fail("Exception expected!"); + } catch (IncorrectResultSizeDataAccessException e) { + assertThat(e.getExpectedSize(), is(1)); + assertThat(e.getActualSize(), is(2)); + } + } + private void assertResultsFound(Iterable result, T... expected) { assertThat(result, is(notNullValue())); diff --git a/src/test/java/org/springframework/data/gemfire/repository/support/GemfireRepositoryFactoryIntegrationTests.java b/src/test/java/org/springframework/data/gemfire/repository/support/GemfireRepositoryFactoryIntegrationTests.java index 7440a9d5..2ae9edaf 100644 --- a/src/test/java/org/springframework/data/gemfire/repository/support/GemfireRepositoryFactoryIntegrationTests.java +++ b/src/test/java/org/springframework/data/gemfire/repository/support/GemfireRepositoryFactoryIntegrationTests.java @@ -38,7 +38,7 @@ public class GemfireRepositoryFactoryIntegrationTests extends AbstractGemfireRep } @Test(expected = IllegalStateException.class) - @SuppressWarnings("unchecked") + @SuppressWarnings({ "unchecked", "rawtypes" }) public void throwsExceptionIfReferencedRegionIsNotConfigured() { GemfireRepositoryFactory factory = new GemfireRepositoryFactory((Iterable) Collections.emptySet(), null);