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.
This commit is contained in:
Oliver Gierke
2012-08-06 15:29:04 +02:00
parent 045c3570ec
commit 1935d35b5f
4 changed files with 67 additions and 7 deletions

View File

@@ -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);
}

View File

@@ -42,4 +42,6 @@ public interface PersonRepository extends CrudRepository<Person, Long> {
Collection<Person> findByFirstnameAndLastname(String firstname, String lastname);
Collection<Person> findByFirstnameOrLastname(String firstname, String lastname);
Person findByLastname(String lastname);
}

View File

@@ -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<Region<?, ?>> 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 <T> void assertResultsFound(Iterable<T> result, T... expected) {
assertThat(result, is(notNullValue()));

View File

@@ -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);