Fixed JIRA issue SGF-247 - boolean-based Repository queries generate UnsupportedOperationException.

This commit is contained in:
John Blum
2014-01-17 20:45:23 -08:00
parent c278676423
commit cd8b1b24ca
6 changed files with 387 additions and 38 deletions

View File

@@ -121,20 +121,22 @@ class Predicates implements Predicate {
*/
@Override
public String toString(String alias) {
Type type = part.getType();
return String.format("%s.%s %s", alias == null ? QueryBuilder.DEFAULT_ALIAS : alias, part.getProperty()
.toDotPath(), toClause(type));
return String.format("%s.%s %s", alias == null ? QueryBuilder.DEFAULT_ALIAS : alias,
part.getProperty().toDotPath(), toClause(type));
}
private String toClause(Type type) {
switch (type) {
case IS_NULL:
case IS_NOT_NULL:
return String.format("%s NULL", getOperator(type));
default:
return String.format("%s $%s", getOperator(type), value.next());
case FALSE:
case TRUE:
return String.format("%1$s %2$s", getOperator(type), Type.TRUE.equals(type));
case IS_NULL:
case IS_NOT_NULL:
return String.format("%s NULL", getOperator(type));
default:
return String.format("%s $%s", getOperator(type), value.next());
}
}
@@ -145,33 +147,39 @@ class Predicates implements Predicate {
* @return
*/
private String getOperator(Type type) {
switch (type) {
case IN:
return "IN SET";
case NOT_IN:
return "NOT IN SET";
case GREATER_THAN:
return ">";
case GREATER_THAN_EQUAL:
return ">=";
case LESS_THAN:
return "<";
case LESS_THAN_EQUAL:
return "<=";
case IS_NOT_NULL:
case NEGATING_SIMPLE_PROPERTY:
return "!=";
case LIKE:
case STARTING_WITH:
case ENDING_WITH:
case CONTAINING:
return "LIKE";
case IS_NULL:
case SIMPLE_PROPERTY:
return "=";
default:
throw new IllegalArgumentException(String.format("Unsupported operator %s!", type));
case IN:
return "IN SET";
case NOT_IN:
return "NOT IN SET";
case GREATER_THAN:
return ">";
case GREATER_THAN_EQUAL:
return ">=";
case LESS_THAN:
return "<";
case LESS_THAN_EQUAL:
return "<=";
case IS_NOT_NULL:
case NEGATING_SIMPLE_PROPERTY:
return "!=";
/*
NOTE unfortunately, 'NOT LIKE' operator is not supported by GemFire's Query/OQL syntax
case NOT_LIKE:
return "NOT LIKE";
*/
case LIKE:
case STARTING_WITH:
case ENDING_WITH:
case CONTAINING:
return "LIKE";
case FALSE:
case IS_NULL:
case SIMPLE_PROPERTY:
case TRUE:
return "=";
default:
throw new IllegalArgumentException(String.format("Unsupported operator %s!", type));
}
}
}

View File

@@ -15,10 +15,12 @@
*/
package org.springframework.data.gemfire.repository.query;
import static org.hamcrest.CoreMatchers.*;
import static org.junit.Assert.*;
import static org.hamcrest.CoreMatchers.is;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertThat;
import java.util.Arrays;
import java.util.Collections;
import java.util.Iterator;
import org.junit.Test;
@@ -29,6 +31,7 @@ import org.springframework.data.repository.query.parser.Part;
*
* @author Oliver Gierke
*/
@SuppressWarnings("unused")
public class PredicatesUnitTests {
@Test
@@ -68,9 +71,24 @@ public class PredicatesUnitTests {
assertThat(predicate.toString(null), is("x.firstname = $1 OR x.lastname = $2"));
}
static class Person {
@Test
public void testBooleanBasedPredicate() {
Part part = new Part("activeTrue", User.class);
Iterator<Integer> indexes = Collections.<Integer>emptyList().iterator();
Predicates predicate = Predicates.create(part, indexes);
assertNotNull(predicate);
assertThat(predicate.toString("user"), is("user.active = true"));
}
static class Person {
String firstname;
String lastname;
}
static class User {
Boolean active;
String username;
}
}

View File

@@ -0,0 +1,116 @@
/*
* Copyright 2010-2013 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.gemfire.repository.sample;
import java.util.Calendar;
import org.springframework.data.annotation.Id;
import org.springframework.data.gemfire.mapping.Region;
import org.springframework.util.Assert;
import org.springframework.util.ObjectUtils;
/**
* The User class represents an authorized user of a service or computer system, etc.
* <p/>
* @author John Blum
* @since 1.3.3 (Spring Data GemFire)
* @since 7.0.1 (GemFire)
*/
@SuppressWarnings("unused")
@Region("Users")
public class User {
private Boolean active = true;
private Calendar since;
private String email;
@Id
private final String username;
public User(final String username) {
Assert.hasText(username, "The username is required!");
this.username = username;
}
public Boolean getActive() {
return active;
}
public boolean isActive() {
return Boolean.TRUE.equals(getActive());
}
public void setActive(final Boolean active) {
this.active = Boolean.TRUE.equals(active);
}
public String getEmail() {
return email;
}
public void setEmail(final String email) {
this.email = email;
}
public Calendar getSince() {
return since;
}
public void setSince(final Calendar since) {
this.since = since;
}
public String getUsername() {
return username;
}
private static boolean equalsIgnoreNull(final Object obj1, final Object obj2) {
return (obj1 == null ? obj2 == null : obj1.equals(obj2));
}
@Override
public boolean equals(final Object obj) {
if (obj == this) {
return true;
}
if (!(obj instanceof User)) {
return false;
}
User that = (User) obj;
return this.getUsername().equals(that.getUsername())
&& ObjectUtils.nullSafeEquals(this.getEmail(), that.getEmail());
}
@Override
public int hashCode() {
int hashValue = 17;
hashValue = 37 * hashValue + ObjectUtils.nullSafeHashCode(getEmail());
hashValue = 37 * hashValue + ObjectUtils.nullSafeHashCode(getUsername());
return hashValue;
}
@Override
public String toString() {
return getUsername();
}
}

View File

@@ -0,0 +1,45 @@
/*
* Copyright 2010-2013 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.gemfire.repository.sample;
import java.util.List;
import org.springframework.data.gemfire.repository.GemfireRepository;
/**
* The UserRepository class is a DAO for accessing and persisting Users.
* <p/>
* @author John Blum
* @see org.springframework.data.gemfire.repository.GemfireRepository
* @since 1.3.3 (Spring Data GemFire)
* @since 7.0.1 (GemFire)
*/
@SuppressWarnings("unused")
public interface UserRepository extends GemfireRepository<User, String> {
//@Query("SELECT DISTINCT * FROM /Users u WHERE u.active = true")
public List<User> findDistinctByActiveTrue();
//@Query("SELECT DISTINCT * FROM /Users u WHERE u.active = false")
public List<User> findDistinctByActiveFalse();
public List<User> findDistinctByUsernameLike(String username);
// NOTE unfortunately, the 'NOT LIKE' operator is unsupported in GemFire's Query/OQL syntax
//public List<User> findDistinctByUsernameNotLike(String username);
}

View File

@@ -0,0 +1,134 @@
/*
* Copyright 2012 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.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 java.util.ArrayList;
import java.util.Arrays;
import java.util.Calendar;
import java.util.List;
import javax.annotation.Resource;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
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.
* <p/>
* @author John Blum
* @see org.junit.Test
* @see org.junit.runner.RunWith
* @see org.springframework.test.context.ContextConfiguration
* @see org.springframework.test.context.junit4.SpringJUnit4ClassRunner
* @since 1.3.3 (Spring Data GemFire)
* @since 7.0.1 (GemFire)
*/
@ContextConfiguration("userRepositoryQueriesIntegrationTest.xml")
@RunWith(SpringJUnit4ClassRunner.class)
@SuppressWarnings("unused")
public class UserRepositoryQueriesIntegrationTest {
@Resource(name = "Users")
private Region users;
@Autowired
private UserRepository userRepository;
protected static void assertQueryResults(final Iterable<User> actualUsers, final String... expectedUsernames) {
assertNotNull("The query did not return any results!", actualUsers);
List<String> actualUsernames = new ArrayList<String>(expectedUsernames.length);
for (User actualUser : actualUsers) {
actualUsernames.add(actualUser.getUsername());
}
assertEquals(expectedUsernames.length, actualUsernames.size());
assertTrue(actualUsernames.containsAll(Arrays.asList(expectedUsernames)));
}
protected static User createUser(final String username) {
return createUser(username, true);
}
protected static User createUser(final String username, final Boolean active) {
return createUser(username, active, Calendar.getInstance(), String.format("%1$s@xcompany.com", username));
}
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;
}
@Before
public void setup() {
assertNotNull("The Users Region cannot be null!", users);
if (users.isEmpty()) {
userRepository.save(createUser("blumj", true));
userRepository.save(createUser("blums", true));
userRepository.save(createUser("blume", false));
userRepository.save(createUser("bloomr", false));
userRepository.save(createUser("handyj", true));
userRepository.save(createUser("handys", false));
userRepository.save(createUser("doej", true));
userRepository.save(createUser("doep", false));
userRepository.save(createUser("doec", false));
assertFalse(users.isEmpty());
assertEquals(9, users.size());
}
}
@Test
public void testQueries() {
List<User> activeUsers = userRepository.findDistinctByActiveTrue();
assertQueryResults(activeUsers, "blumj", "blums", "handyj", "doej");
List<User> inactiveUsers = userRepository.findDistinctByActiveFalse();
assertQueryResults(inactiveUsers, "blume", "bloomr", "handys", "doep", "doec");
List<User> blumUsers = userRepository.findDistinctByUsernameLike("blum%");
assertQueryResults(blumUsers, "blumj", "blums", "blume");
/*
List<User> nonHandyUsers = userRepository.findDistinctByUsernameNotLike("handy%");
assertQueryResults(nonHandyUsers, "blumj", "blums", "blume", "bloomr", "doej", "doep", "doec");
*/
}
}

View File

@@ -0,0 +1,28 @@
<?xml version="1.0" encoding="utf-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:gfe="http://www.springframework.org/schema/gemfire"
xmlns:gfe-data="http://www.springframework.org/schema/data/gemfire"
xmlns:util="http://www.springframework.org/schema/util"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/gemfire http://www.springframework.org/schema/gemfire/spring-gemfire.xsd
http://www.springframework.org/schema/data/gemfire http://www.springframework.org/schema/data/gemfire/spring-data-gemfire.xsd
http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd
">
<util:properties id="peerCacheConfigurationSettings">
<prop key="name">repositoryQueriesTest</prop>
<prop key="locators">localhost[11235]</prop>
<prop key="log-level">config</prop>
<prop key="mcast-port">0</prop>
<prop key="start-locator">localhost[11235]</prop>
</util:properties>
<gfe:cache/>
<gfe:replicated-region id="Users" persistent="false"/>
<gfe-data:repositories base-package="org.springframework.data.gemfire.repository.sample"/>
</beans>