Fixes JIRA issue SGF-273 - OrderBy (static) and Sort parameter (dynamic) Repository Queries do not work.

This commit is contained in:
John Blum
2014-04-28 15:37:30 -07:00
parent 8fd0b0bf6d
commit 0c25e14be9
10 changed files with 310 additions and 99 deletions

View File

@@ -44,10 +44,9 @@ class GemfireQueryCreator extends AbstractQueryCreator<QueryString, Predicates>
* @param entity must not be {@literal null}.
*/
public GemfireQueryCreator(PartTree tree, GemfirePersistentEntity<?> entity) {
super(tree);
this.query = new QueryBuilder(entity);
this.query = new QueryBuilder(entity, tree);
this.indexes = new IndexProvider();
}
@@ -57,7 +56,6 @@ class GemfireQueryCreator extends AbstractQueryCreator<QueryString, Predicates>
*/
@Override
public QueryString createQuery(Sort dynamicSort) {
this.indexes = new IndexProvider();
return super.createQuery(dynamicSort);
}
@@ -95,11 +93,10 @@ class GemfireQueryCreator extends AbstractQueryCreator<QueryString, Predicates>
*/
@Override
protected QueryString complete(Predicates criteria, Sort sort) {
QueryString result = query.create(criteria);
QueryString result = query.create(criteria).orderBy(sort);
if (LOG.isDebugEnabled()) {
LOG.debug("Created query: " + result.toString());
LOG.debug(String.format("Created Query '%1$s'", result.toString()));
}
return result;
@@ -119,6 +116,7 @@ class GemfireQueryCreator extends AbstractQueryCreator<QueryString, Predicates>
*/
@Override
public boolean hasNext() {
// TODO really?
return index <= Integer.MAX_VALUE;
}
@@ -140,4 +138,5 @@ class GemfireQueryCreator extends AbstractQueryCreator<QueryString, Predicates>
throw new UnsupportedOperationException();
}
}
}

View File

@@ -15,13 +15,15 @@
*/
package org.springframework.data.gemfire.repository.query;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import org.springframework.data.domain.Sort;
import org.springframework.data.gemfire.GemfireTemplate;
import org.springframework.data.repository.query.ParametersParameterAccessor;
import org.springframework.data.repository.query.RepositoryQuery;
import org.springframework.data.repository.query.parser.Part;
import org.springframework.data.repository.query.parser.Part.Type;
import org.springframework.data.repository.query.parser.PartTree;
/**
@@ -71,35 +73,31 @@ public class PartTreeGemfireRepositoryQuery extends GemfireRepositoryQuery {
}
private Object[] prepareStringParameters(Object[] parameters) {
Iterator<Part> partsIterator = tree.getParts().iterator();
List<Object> stringParameters = new ArrayList<Object>(parameters.length);
Iterator<Part> iterator = tree.getParts().iterator();
Object[] result = new Object[parameters.length];
for (int i = 0; i < parameters.length; i++) {
Object parameter = parameters[i];
if (parameter == null) {
result[i] = parameter;
continue;
for (Object parameter : parameters) {
if (parameter == null || parameter instanceof Sort) {
stringParameters.add(parameter);
}
Type type = iterator.next().getType();
switch (type) {
case CONTAINING:
result[i] = String.format("%%%s%%", parameter.toString());
break;
case STARTING_WITH:
result[i] = String.format("%s%%", parameter.toString());
break;
case ENDING_WITH:
result[i] = String.format("%%%s", parameter.toString());
break;
default:
result[i] = parameter;
else {
switch (partsIterator.next().getType()) {
case CONTAINING:
stringParameters.add(String.format("%%%s%%", parameter.toString()));
break;
case STARTING_WITH:
stringParameters.add(String.format("%s%%", parameter.toString()));
break;
case ENDING_WITH:
stringParameters.add(String.format("%%%s", parameter.toString()));
break;
default:
stringParameters.add(parameter);
}
}
}
return result;
return stringParameters.toArray();
}
}

View File

@@ -20,9 +20,12 @@ import org.springframework.data.repository.query.parser.PartTree;
import org.springframework.util.Assert;
/**
*
* The QueryBuilder class is used to build a QueryString.
*
* @author Oliver Gierke
* @author David Turanski
* @author John Blum
* @see org.springframework.data.gemfire.repository.query.QueryString
*/
class QueryBuilder {
@@ -31,17 +34,18 @@ class QueryBuilder {
private final String query;
public QueryBuilder(String source) {
Assert.hasText(source);
Assert.hasText(source, "The OQL Query string must be specified.");
this.query = source;
}
public QueryBuilder(GemfirePersistentEntity<?> entity) {
this(String.format("SELECT * FROM /%s %s", entity.getRegionName(), DEFAULT_ALIAS));
public QueryBuilder(GemfirePersistentEntity<?> entity, PartTree tree) {
this(String.format(tree.isDistinct() ? "SELECT DISTINCT * FROM /%1$s %2$s" : "SELECT * FROM /%1$s %2$s",
entity.getRegionName(), DEFAULT_ALIAS));
}
public QueryString create(Predicate predicate) {
return new QueryString(query + " WHERE " + predicate.toString(DEFAULT_ALIAS));
return new QueryString(predicate != null ? String.format("%1$s WHERE %2$s", query,
predicate.toString(DEFAULT_ALIAS)) : query);
}
/*
@@ -52,4 +56,5 @@ class QueryBuilder {
public String toString() {
return query;
}
}

View File

@@ -21,11 +21,12 @@ import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import com.gemstone.gemfire.cache.Region;
import org.springframework.data.domain.Sort;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
import com.gemstone.gemfire.cache.Region;
/**
* Value object to work with OQL query strings.
*
@@ -33,7 +34,7 @@ import org.springframework.util.StringUtils;
* @author David Turanski
* @author John Blum
*/
class QueryString {
public class QueryString {
private static final String IN_PATTERN = "(?<=IN (SET|LIST) )\\$\\d";
private static final String IN_PARAMETER_PATTERN = "(?<=IN (SET|LIST) \\$)\\d";
@@ -44,7 +45,7 @@ class QueryString {
/**
* Creates a {@link QueryString} from the given {@link String} query.
*
* @param source
* @param source a String containing the OQL Query.
*/
public QueryString(String source) {
Assert.hasText(source, "The OQL statement (Query) to execute must be specified!");
@@ -56,8 +57,8 @@ class QueryString {
*
* @param domainClass must not be {@literal null}.
*/
@SuppressWarnings("unused")
public QueryString(Class<?> domainClass) {
@SuppressWarnings("unused")
public QueryString(Class<?> domainClass) {
this(domainClass, false);
}
@@ -68,41 +69,45 @@ class QueryString {
* @param isCountQuery indicates if this is a count query
*/
public QueryString(Class<?> domainClass, boolean isCountQuery) {
this(String.format(isCountQuery ? "SELECT count(*) FROM /%s" : "SELECT * FROM /%s", domainClass.getSimpleName()));
}
/**
* Replaces the domain classes referenced inside the current query with the given {@link Region}.
*
* @param domainClass must not be {@literal null}.
* @param region must not be {@literal null}.
* @return
*/
@SuppressWarnings("unused")
public QueryString forRegion(Class<?> domainClass, Region<?, ?> region) {
return new QueryString(query.replaceAll(REGION_PATTERN, region.getFullPath()));
this(String.format(isCountQuery ? "SELECT count(*) FROM /%s" : "SELECT * FROM /%s",
domainClass.getSimpleName()));
}
/**
* Binds the given values to the {@literal IN} parameter keyword by expanding the given values into a comma-separated
* {@link String}.
*
*
* @param values the values to bind, returns the {@link QueryString} as is if {@literal null} is given.
* @return
* @return a Query String having "in" parameters bound with values.
*/
public QueryString bindIn(Collection<?> values) {
if (values != null) {
String valueString = StringUtils.collectionToDelimitedString(values, ", ", "'", "'");
return new QueryString(query.replaceFirst(IN_PATTERN, String.format("(%s)", valueString)));
}
public QueryString bindIn(Collection<?> values) {
if (values != null) {
String valueString = StringUtils.collectionToDelimitedString(values, ", ", "'", "'");
return new QueryString(query.replaceFirst(IN_PATTERN, String.format("(%s)", valueString)));
}
return this;
}
return this;
}
/**
* Replaces the domain classes referenced inside the current query with the given {@link Region}.
*
* @param domainClass the class type of the GemFire persistent entity to query; must not be {@literal null}.
* @param region the GemFire Region in which to query; must not be {@literal null}.
* @return a Query String with the FROM clause in the OQL statement evaluated and replaced with
* the fully-qualified Region to query.
* @see com.gemstone.gemfire.cache.Region
*/
@SuppressWarnings("unused")
public QueryString forRegion(Class<?> domainClass, Region<?, ?> region) {
return new QueryString(query.replaceAll(REGION_PATTERN, region.getFullPath()));
}
/**
* Returns the parameter indexes used in this query.
*
* @return the parameter indexes used in this query or an empty {@link Iterable} if none are used.
* @see java.lang.Iterable
*/
public Iterable<Integer> getInParameterIndexes() {
Pattern pattern = Pattern.compile(IN_PARAMETER_PATTERN);
@@ -116,6 +121,31 @@ class QueryString {
return result;
}
/**
* Appends the Sort order to this GemFire OQL Query string.
*
* @param sort the Sort object indicating the order by criteria.
* @return this QueryString with an ORDER BY clause if the Sort object is not null, or this QueryString as-is
* if the Sort object is null.
* @see org.springframework.data.domain.Sort
* @see org.springframework.data.gemfire.repository.query.QueryString
*/
public QueryString orderBy(Sort sort) {
if (sort != null) {
StringBuilder orderClause = new StringBuilder("ORDER BY ");
int count = 0;
for (Sort.Order order : sort) {
orderClause.append(count++ > 0 ? ", " : "");
orderClause.append(String.format("%1$s %2$s", order.getProperty(), order.getDirection()));
}
return new QueryString(String.format("%1$s %2$s", this.query, orderClause.toString()));
}
return this;
}
/*
* (non-Javadoc)
* @see java.lang.Object#toString()

View File

@@ -10,6 +10,7 @@ import org.springframework.data.gemfire.GemfireCallback;
import org.springframework.data.gemfire.GemfireTemplate;
import org.springframework.data.gemfire.repository.GemfireRepository;
import org.springframework.data.gemfire.repository.Wrapper;
import org.springframework.data.gemfire.repository.query.QueryString;
import org.springframework.data.repository.core.EntityInformation;
import org.springframework.util.Assert;
@@ -111,32 +112,15 @@ public class SimpleGemfireRepository<T, ID extends Serializable> implements Gemf
*/
@Override
public Iterable<T> findAll(final Sort sort) {
SelectResults<T> selectResults = template.find(String.format("SELECT * FROM %1$s%2$s",
template.getRegion().getFullPath(), toString(sort)));
QueryString query = new QueryString("SELECT * FROM /RegionPlaceholder")
.forRegion(entityInformation.getJavaType(), template.getRegion())
.orderBy(sort);
SelectResults<T> selectResults = template.find(query.toString());
return selectResults.asList();
}
/*
* (non-Javadoc)
*
* @see org.springframework.data.domain.Sort
*/
private String toString(final Sort sort) {
if (sort != null) {
StringBuilder orderClause = new StringBuilder(" ORDER BY ");
int count = 0;
for (Sort.Order order : sort) {
orderClause.append(count++ > 0 ? ", " : "");
orderClause.append(String.format("%1$s %2$s", order.getProperty(), order.getDirection()));
}
return orderClause.toString();
}
return "";
}
/*
* (non-Javadoc)
*