From 1920ce4ba190d543160e431eb8cac495dd88662b Mon Sep 17 00:00:00 2001 From: John Blum Date: Mon, 16 Jun 2014 20:01:34 -0700 Subject: [PATCH] Fixes JIRA issue SGF-290 handling custom OQL statements specified with an @Query annotated Repository method that returns neither a Collection nor a Entity-based result, such as the case with SELECT count(*) FROM /Region ... based queries returning an Integer count. --- .../StringBasedGemfireRepositoryQuery.java | 65 +++++++++++-------- .../repository/sample/UserRepository.java | 12 ++-- .../UserRepositoryQueriesIntegrationTest.java | 37 +++++++---- .../userRepositoryQueriesIntegrationTest.xml | 4 +- 4 files changed, 73 insertions(+), 45 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 a69b6abb..ceca605d 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,8 @@ import java.util.Arrays; import java.util.Collection; import java.util.Collections; +import com.gemstone.gemfire.cache.query.SelectResults; + import org.springframework.dao.IncorrectResultSizeDataAccessException; import org.springframework.data.gemfire.GemfireTemplate; import org.springframework.data.repository.query.ParametersParameterAccessor; @@ -26,9 +28,6 @@ import org.springframework.util.Assert; import org.springframework.util.CollectionUtils; import org.springframework.util.StringUtils; -import com.gemstone.gemfire.cache.query.SelectResults; -import com.gemstone.gemfire.cache.query.internal.ResultsBag; - /** * {@link GemfireRepositoryQuery} using plain {@link String} based OQL queries. *

@@ -111,51 +110,61 @@ public class StringBasedGemfireRepositoryQuery extends GemfireRepositoryQuery { */ @Override public Object execute(Object[] parameters) { - ParametersParameterAccessor accessor = new ParametersParameterAccessor(method.getParameters(), parameters); + ParametersParameterAccessor parameterAccessor = new ParametersParameterAccessor(method.getParameters(), parameters); - QueryString query = (isUserDefinedQuery() ? this.query : - this.query.forRegion(method.getEntityInformation().getJavaType(), template.getRegion())); + QueryString query = (isUserDefinedQuery() ? this.query : this.query.forRegion( + method.getEntityInformation().getJavaType(), template.getRegion())); for (Integer index : query.getInParameterIndexes()) { - query = query.bindIn(toCollection(accessor.getBindableValue(index - 1))); + query = query.bindIn(toCollection(parameterAccessor.getBindableValue(index - 1))); } 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("Unsupported query: " + query.toString()); - } - } + 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 if (isSingleResultNonEntityQuery(method, result)) { + return result.iterator().next(); + } + else { + throw new IllegalStateException("Unsupported query: " + query.toString()); + } + } - /** + + boolean isSingleResultNonEntityQuery(final GemfireQueryMethod method, final Collection result) { + return (!method.isCollectionQuery() && method.getReturnedObjectType() != null + && !Void.TYPE.equals(method.getReturnedObjectType()) && result != null && result.size() == 1); + } + + /** * Returns the given object as a Collection. Collections will be returned as is, Arrays will be converted into a * Collection and all other objects will be wrapped into a single-element Collection. - *

+ * * @param source the resulting object from the GemFire Query. * @return the querying resulting object as a Collection. * @see java.util.Arrays#asList(Object[]) * @see java.util.Collection * @see org.springframework.util.CollectionUtils#arrayToList(Object) + * @see com.gemstone.gemfire.cache.query.SelectResults */ - Collection toCollection(Object source) { + Collection toCollection(final Object source) { if (source instanceof SelectResults) { return ((SelectResults) source).asList(); } - if (source instanceof ResultsBag) { - return ((ResultsBag) source).asList(); - } - if (source instanceof Collection) { return (Collection) source; } diff --git a/src/test/java/org/springframework/data/gemfire/repository/sample/UserRepository.java b/src/test/java/org/springframework/data/gemfire/repository/sample/UserRepository.java index 601b40c4..09c0a7bd 100644 --- a/src/test/java/org/springframework/data/gemfire/repository/sample/UserRepository.java +++ b/src/test/java/org/springframework/data/gemfire/repository/sample/UserRepository.java @@ -19,6 +19,7 @@ package org.springframework.data.gemfire.repository.sample; import java.util.List; import org.springframework.data.gemfire.repository.GemfireRepository; +import org.springframework.data.gemfire.repository.Query; /** * The UserRepository class is a DAO for accessing and persisting Users. @@ -33,14 +34,17 @@ import org.springframework.data.gemfire.repository.GemfireRepository; public interface UserRepository extends GemfireRepository { //@Query("SELECT DISTINCT * FROM /Users u WHERE u.active = true") - public List findDistinctByActiveTrue(); + List findDistinctByActiveTrue(); //@Query("SELECT DISTINCT * FROM /Users u WHERE u.active = false") - public List findDistinctByActiveFalse(); + List findDistinctByActiveFalse(); - public List findDistinctByUsernameLike(String username); + List findDistinctByUsernameLike(String username); // NOTE unfortunately, the 'NOT LIKE' operator is unsupported in GemFire's Query/OQL syntax - //public List findDistinctByUsernameNotLike(String username); + //List findDistinctByUsernameNotLike(String username); + + @Query("SELECT count(*) FROM /Users u WHERE u.username LIKE $1") + Integer countUsersByUsernameLike(String username); } diff --git a/src/test/java/org/springframework/data/gemfire/repository/sample/UserRepositoryQueriesIntegrationTest.java b/src/test/java/org/springframework/data/gemfire/repository/sample/UserRepositoryQueriesIntegrationTest.java index be52c90d..ac0e6c40 100644 --- a/src/test/java/org/springframework/data/gemfire/repository/sample/UserRepositoryQueriesIntegrationTest.java +++ b/src/test/java/org/springframework/data/gemfire/repository/sample/UserRepositoryQueriesIntegrationTest.java @@ -16,10 +16,7 @@ package org.springframework.data.gemfire.repository.sample; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertTrue; +import static org.junit.Assert.*; import java.util.ArrayList; import java.util.Arrays; @@ -27,6 +24,8 @@ import java.util.Calendar; import java.util.List; import javax.annotation.Resource; +import com.gemstone.gemfire.cache.Region; + import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -34,8 +33,6 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; -import com.gemstone.gemfire.cache.Region; - /** * The RepositoryQueriesTest class is a test suite of test cases testing the GemFire Query capability of Spring Data * GemFire Repositories. @@ -47,8 +44,8 @@ import com.gemstone.gemfire.cache.Region; * @see org.springframework.test.context.junit4.SpringJUnit4ClassRunner * @since 1.3.3 */ -@ContextConfiguration("userRepositoryQueriesIntegrationTest.xml") @RunWith(SpringJUnit4ClassRunner.class) +@ContextConfiguration("userRepositoryQueriesIntegrationTest.xml") @SuppressWarnings("unused") public class UserRepositoryQueriesIntegrationTest { @@ -81,17 +78,19 @@ public class UserRepositoryQueriesIntegrationTest { protected static User createUser(final String username, final Boolean active, final Calendar since, final String email) { User user = new User(username); - user.setActive(active); user.setEmail(email); user.setSince(since); - return user; } + protected static int toIntValue(final Integer value) { + return (value == null ? 0 : value.intValue()); + } + @Before public void setup() { - assertNotNull("The Users Region cannot be null!", users); + assertNotNull("The 'Users' GemFire Cache Region cannot be null!", users); if (users.isEmpty()) { userRepository.save(createUser("blumj", true)); @@ -110,7 +109,7 @@ public class UserRepositoryQueriesIntegrationTest { } @Test - public void testQueries() { + public void testMultiResultQueries() { List activeUsers = userRepository.findDistinctByActiveTrue(); assertQueryResults(activeUsers, "blumj", "blums", "handyj", "doej"); @@ -130,4 +129,20 @@ public class UserRepositoryQueriesIntegrationTest { */ } + @Test + public void testNonCollectionNonEntityResultQueries() { + Integer count = userRepository.countUsersByUsernameLike("doe%"); + + assertEquals(3, toIntValue(count)); + + count = userRepository.countUsersByUsernameLike("handy%"); + + assertEquals(2, toIntValue(count)); + + count = userRepository.countUsersByUsernameLike("smith%"); + + assertNotNull(count); + assertEquals(0, toIntValue(count)); + } + } diff --git a/src/test/resources/org/springframework/data/gemfire/repository/sample/userRepositoryQueriesIntegrationTest.xml b/src/test/resources/org/springframework/data/gemfire/repository/sample/userRepositoryQueriesIntegrationTest.xml index cb971e3d..b5a2db3f 100644 --- a/src/test/resources/org/springframework/data/gemfire/repository/sample/userRepositoryQueriesIntegrationTest.xml +++ b/src/test/resources/org/springframework/data/gemfire/repository/sample/userRepositoryQueriesIntegrationTest.xml @@ -14,9 +14,9 @@ "> - repositoryQueriesTest - config + UserRepositoryQueriesIntegrationTest 0 + config