SGF-507 - Handle case-insensitive OQL queries defined as Repository query methods.
(cherry picked from commit 96a5990749a76f1369cecaa54f4d98196f6688ac) Signed-off-by: John Blum <jblum@pivotal.io>
This commit is contained in:
8
.gitignore
vendored
8
.gitignore
vendored
@@ -1,8 +1,8 @@
|
||||
.DS_Store
|
||||
target
|
||||
bin
|
||||
build
|
||||
out
|
||||
bin/
|
||||
build/
|
||||
out/
|
||||
target/
|
||||
.gradle
|
||||
.springBeans
|
||||
*.iml
|
||||
|
||||
@@ -109,6 +109,10 @@ gets to verbose you can annotate the query methods with `@Query` as seen for met
|
||||
| `findByFirstnameNotIn(Collection<String> x)`
|
||||
| `x.firstname NOT IN SET $1`
|
||||
|
||||
| `IgnoreCase`
|
||||
| `findByFirstnameIgnoreCase(String firstName)`
|
||||
| `x.firstname.equalsIgnoreCase($1)`
|
||||
|
||||
| (No keyword)
|
||||
| `findByFirstname(String name)`
|
||||
| `x.firstname = $1`
|
||||
|
||||
@@ -24,7 +24,7 @@ import org.springframework.data.mapping.model.SimpleTypeHolder;
|
||||
import org.springframework.data.util.TypeInformation;
|
||||
|
||||
/**
|
||||
*
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
* @author John Blum
|
||||
*/
|
||||
@@ -58,7 +58,7 @@ public class GemfireMappingContext extends AbstractMappingContext<GemfirePersist
|
||||
@Override
|
||||
protected GemfirePersistentProperty createPersistentProperty(Field field, PropertyDescriptor descriptor,
|
||||
GemfirePersistentEntity<?> owner, SimpleTypeHolder simpleTypeHolder) {
|
||||
|
||||
return new GemfirePersistentProperty(field, descriptor, owner, simpleTypeHolder);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -36,21 +36,22 @@ public class Regions implements Iterable<Region<?, ?>> {
|
||||
|
||||
private final Map<String, Region<?, ?>> regions;
|
||||
|
||||
private final MappingContext<? extends GemfirePersistentEntity<?>, ?> context;
|
||||
private final MappingContext<? extends GemfirePersistentEntity<?>, ?> mappingContext;
|
||||
|
||||
/**
|
||||
* Creates a new {@link Regions} wrapper for the given {@link Region}s and
|
||||
* {@link MappingContext}.
|
||||
*
|
||||
*
|
||||
* @param regions must not be {@literal null}.
|
||||
* @param context must not be {@literal null}.
|
||||
* @param mappingContext must not be {@literal null}.
|
||||
*/
|
||||
public Regions(Iterable<Region<?, ?>> regions, MappingContext<? extends GemfirePersistentEntity<?>, ?> context) {
|
||||
public Regions(Iterable<Region<?, ?>> regions,
|
||||
MappingContext<? extends GemfirePersistentEntity<?>, ?> mappingContext) {
|
||||
|
||||
Assert.notNull(regions);
|
||||
Assert.notNull(context);
|
||||
Assert.notNull(regions, "Regions must not be null");
|
||||
Assert.notNull(mappingContext, "MappingContext must not be null");
|
||||
|
||||
Map<String, com.gemstone.gemfire.cache.Region<?, ?>> regionMap = new HashMap<String, Region<?, ?>>();
|
||||
Map<String, Region<?, ?>> regionMap = new HashMap<String, Region<?, ?>>();
|
||||
|
||||
for (Region<?, ?> region : regions) {
|
||||
regionMap.put(region.getName(), region);
|
||||
@@ -58,7 +59,7 @@ public class Regions implements Iterable<Region<?, ?>> {
|
||||
}
|
||||
|
||||
this.regions = Collections.unmodifiableMap(regionMap);
|
||||
this.context = context;
|
||||
this.mappingContext = mappingContext;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -67,15 +68,15 @@ public class Regions implements Iterable<Region<?, ?>> {
|
||||
* information is found.
|
||||
*
|
||||
* @param <T> the Region value class type.
|
||||
* @param type must not be {@literal null}.
|
||||
* @param entityType must not be {@literal null}.
|
||||
* @return the {@link Region} the given type is mapped to.
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public <T> Region<?, T> getRegion(Class<T> type) {
|
||||
Assert.notNull(type);
|
||||
public <T> Region<?, T> getRegion(Class<T> entityType) {
|
||||
Assert.notNull(entityType, "entityType must not be null");
|
||||
|
||||
GemfirePersistentEntity<?> entity = context.getPersistentEntity(type);
|
||||
String regionName = (entity != null ? entity.getRegionName() : type.getSimpleName());
|
||||
GemfirePersistentEntity<?> entity = mappingContext.getPersistentEntity(entityType);
|
||||
String regionName = (entity != null ? entity.getRegionName() : entityType.getSimpleName());
|
||||
|
||||
return (Region<?, T>) regions.get(regionName);
|
||||
}
|
||||
@@ -90,18 +91,18 @@ public class Regions implements Iterable<Region<?, ?>> {
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public <S, T> Region<S, T> getRegion(String namePath) {
|
||||
Assert.notNull(namePath);
|
||||
Assert.hasText(namePath, "Region name/path is required");
|
||||
|
||||
return (Region<S, T>) regions.get(namePath);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
*
|
||||
* @see java.lang.Iterable#iterator()
|
||||
*/
|
||||
@Override
|
||||
public Iterator<Region<?, ?>> iterator() {
|
||||
return regions.values().iterator();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -27,30 +27,32 @@ import org.springframework.data.repository.query.parser.PartTree;
|
||||
|
||||
/**
|
||||
* Query creator to create {@link QueryString} instances.
|
||||
*
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
* @author John Blum
|
||||
*/
|
||||
class GemfireQueryCreator extends AbstractQueryCreator<QueryString, Predicates> {
|
||||
|
||||
private static final Log LOG = LogFactory.getLog(GemfireQueryCreator.class);
|
||||
|
||||
private final QueryBuilder query;
|
||||
private Iterator<Integer> indexes;
|
||||
|
||||
private final QueryBuilder queryBuilder;
|
||||
|
||||
/**
|
||||
* Creates a new {@link GemfireQueryCreator} using the given {@link PartTree} and domain class.
|
||||
*
|
||||
*
|
||||
* @param tree must not be {@literal null}.
|
||||
* @param entity must not be {@literal null}.
|
||||
*/
|
||||
public GemfireQueryCreator(PartTree tree, GemfirePersistentEntity<?> entity) {
|
||||
super(tree);
|
||||
|
||||
this.query = new QueryBuilder(entity, tree);
|
||||
this.queryBuilder = new QueryBuilder(entity, tree);
|
||||
this.indexes = new IndexProvider();
|
||||
}
|
||||
|
||||
/*
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.repository.query.parser.AbstractQueryCreator#createQuery(org.springframework.data.domain.Sort)
|
||||
*/
|
||||
@@ -69,7 +71,7 @@ class GemfireQueryCreator extends AbstractQueryCreator<QueryString, Predicates>
|
||||
return Predicates.create(part, this.indexes);
|
||||
}
|
||||
|
||||
/*
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.repository.query.parser.AbstractQueryCreator#and(org.springframework.data.repository.query.parser.Part, java.lang.Object, java.util.Iterator)
|
||||
*/
|
||||
@@ -78,7 +80,7 @@ class GemfireQueryCreator extends AbstractQueryCreator<QueryString, Predicates>
|
||||
return base.and(Predicates.create(part, this.indexes));
|
||||
}
|
||||
|
||||
/*
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.repository.query.parser.AbstractQueryCreator#or(java.lang.Object, java.lang.Object)
|
||||
*/
|
||||
@@ -87,21 +89,28 @@ class GemfireQueryCreator extends AbstractQueryCreator<QueryString, Predicates>
|
||||
return base.or(criteria);
|
||||
}
|
||||
|
||||
/*
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.repository.query.parser.AbstractQueryCreator#complete(java.lang.Object, org.springframework.data.domain.Sort)
|
||||
*/
|
||||
@Override
|
||||
protected QueryString complete(Predicates criteria, Sort sort) {
|
||||
QueryString result = query.create(criteria).orderBy(sort);
|
||||
QueryString query = queryBuilder.create(criteria).orderBy(sort);
|
||||
|
||||
if (LOG.isDebugEnabled()) {
|
||||
LOG.debug(String.format("Created Query '%1$s'", result.toString()));
|
||||
LOG.debug(String.format("Created Query '%1$s'", query.toString()));
|
||||
}
|
||||
|
||||
return result;
|
||||
return query;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link IndexProvider} is an {@link Iterator} providing sequentially numbered placeholders (starting at 1),
|
||||
* in a generated GemFire OQL statement corresponding to all possible arguments passed to
|
||||
* the query's indexed parameters.
|
||||
*
|
||||
* @see java.util.Iterator
|
||||
*/
|
||||
private static class IndexProvider implements Iterator<Integer> {
|
||||
|
||||
private int index;
|
||||
@@ -110,17 +119,17 @@ class GemfireQueryCreator extends AbstractQueryCreator<QueryString, Predicates>
|
||||
this.index = 1;
|
||||
}
|
||||
|
||||
/*
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see java.util.Iterator#hasNext()
|
||||
*/
|
||||
@Override
|
||||
@SuppressWarnings("all")
|
||||
public boolean hasNext() {
|
||||
// TODO really?
|
||||
return index <= Integer.MAX_VALUE;
|
||||
return (index <= Integer.MAX_VALUE);
|
||||
}
|
||||
|
||||
/*
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see java.util.Iterator#next()
|
||||
*/
|
||||
@@ -129,7 +138,7 @@ class GemfireQueryCreator extends AbstractQueryCreator<QueryString, Predicates>
|
||||
return index++;
|
||||
}
|
||||
|
||||
/*
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see java.util.Iterator#remove()
|
||||
*/
|
||||
@@ -138,5 +147,4 @@ class GemfireQueryCreator extends AbstractQueryCreator<QueryString, Predicates>
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -36,7 +36,7 @@ import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* GemFire specific {@link QueryMethod}.
|
||||
*
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
* @author John Blum
|
||||
* @see org.springframework.data.repository.query.QueryMethod
|
||||
@@ -50,7 +50,7 @@ public class GemfireQueryMethod extends QueryMethod {
|
||||
|
||||
/**
|
||||
* Creates a new {@link GemfireQueryMethod} from the given {@link Method} and {@link RepositoryMetadata}.
|
||||
*
|
||||
*
|
||||
* @param method must not be {@literal null}.
|
||||
* @param metadata must not be {@literal null}.
|
||||
* @param factory must not be {@literal null}.
|
||||
@@ -77,11 +77,11 @@ public class GemfireQueryMethod extends QueryMethod {
|
||||
* @see org.springframework.data.domain.Pageable
|
||||
* @see java.lang.reflect.Method#getParameterTypes()
|
||||
*/
|
||||
private void assertNonPagingQueryMethod(final Method method) {
|
||||
private void assertNonPagingQueryMethod(Method method) {
|
||||
for (Class<?> type : method.getParameterTypes()) {
|
||||
if (Pageable.class.isAssignableFrom(type)) {
|
||||
throw new IllegalStateException(String.format("Pagination is not supported by GemFire Repositories!"
|
||||
+ " Offending method: %1$s", method.toString()));
|
||||
throw new IllegalStateException(String.format("Pagination is not supported by GemFire Repositories;"
|
||||
+ " Offending method: %1$s", method.getName()));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -108,7 +108,7 @@ public class GemfireQueryMethod extends QueryMethod {
|
||||
|
||||
/**
|
||||
* Returns the annotated query for the query method if present.
|
||||
*
|
||||
*
|
||||
* @return the annotated query or {@literal null} in case it's empty or not present.
|
||||
* @see org.springframework.data.gemfire.repository.Query
|
||||
* @see java.lang.reflect.Method#getAnnotation(Class)
|
||||
|
||||
@@ -3,4 +3,5 @@ package org.springframework.data.gemfire.repository.query;
|
||||
interface Predicate {
|
||||
|
||||
String toString(String alias);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -27,7 +27,7 @@ class Predicates implements Predicate {
|
||||
|
||||
/**
|
||||
* Creates a new {@link Predicates} wrapper instance.
|
||||
*
|
||||
*
|
||||
* @param predicate must not be {@literal null}.
|
||||
*/
|
||||
private Predicates(Predicate predicate) {
|
||||
@@ -40,23 +40,22 @@ class Predicates implements Predicate {
|
||||
|
||||
/**
|
||||
* Creates a new Predicate for the given {@link Part} and index iterator.
|
||||
*
|
||||
*
|
||||
* @param part must not be {@literal null}.
|
||||
* @param value must not be {@literal null}.
|
||||
* @return
|
||||
* @param indexes must not be {@literal null}.
|
||||
* @return an instance of {@link Predicates} wrapping the WHERE clause condition expression ({@link Part}).
|
||||
*/
|
||||
public static Predicates create(Part part, Iterator<Integer> value) {
|
||||
return create(new AtomicPredicate(part, value));
|
||||
public static Predicates create(Part part, Iterator<Integer> indexes) {
|
||||
return create(new AtomicPredicate(part, indexes));
|
||||
}
|
||||
|
||||
/**
|
||||
* And-concatenates the given {@link Predicate} to the current one.
|
||||
*
|
||||
*
|
||||
* @param predicate must not be {@literal null}.
|
||||
* @return
|
||||
* @return an instance of {@link Predicates} wrapping an AND condition.
|
||||
*/
|
||||
public Predicates and(final Predicate predicate) {
|
||||
|
||||
return create(new Predicate() {
|
||||
@Override
|
||||
public String toString(String alias) {
|
||||
@@ -67,12 +66,11 @@ class Predicates implements Predicate {
|
||||
|
||||
/**
|
||||
* Or-concatenates the given {@link Predicate} to the current one.
|
||||
*
|
||||
*
|
||||
* @param predicate must not be {@literal null}.
|
||||
* @return
|
||||
* @return an instance of {@link Predicates} wrapping an OR condition.
|
||||
*/
|
||||
public Predicates or(final Predicate predicate) {
|
||||
|
||||
return create(new Predicate() {
|
||||
@Override
|
||||
public String toString(String alias) {
|
||||
@@ -92,66 +90,96 @@ class Predicates implements Predicate {
|
||||
|
||||
/**
|
||||
* Predicate to create a predicate expression for a {@link Part}.
|
||||
*
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
public static class AtomicPredicate implements Predicate {
|
||||
|
||||
private final Iterator<Integer> indexes;
|
||||
|
||||
private final Part part;
|
||||
private final Iterator<Integer> value;
|
||||
|
||||
/**
|
||||
* Creates a new {@link AtomicPredicate}.
|
||||
*
|
||||
*
|
||||
* @param part must not be {@literal null}.
|
||||
* @param value must not be {@literal null}.
|
||||
* @param indexes must not be {@literal null}.
|
||||
*/
|
||||
public AtomicPredicate(Part part, Iterator<Integer> value) {
|
||||
|
||||
Assert.notNull(part);
|
||||
Assert.notNull(value);
|
||||
public AtomicPredicate(Part part, Iterator<Integer> indexes) {
|
||||
Assert.notNull(part, "Query Predicate Part must not be null");
|
||||
Assert.notNull(indexes, "Iterator of numeric, indexed query parameter placeholders must not be null");
|
||||
|
||||
this.part = part;
|
||||
this.value = value;
|
||||
this.indexes = indexes;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
/**
|
||||
* Builds a conditional expression for the entity property in the WHERE clause of the GemFire OQL
|
||||
* query statement.
|
||||
*
|
||||
* @see org.springframework.data.gemfire.repository.query.Predicate#toString(java.lang.String)
|
||||
*/
|
||||
@Override
|
||||
public String toString(String alias) {
|
||||
Type type = part.getType();
|
||||
if (isIgnoreCase()) {
|
||||
return String.format("%s.equalsIgnoreCase($%d)", resolveProperty(alias), indexes.next());
|
||||
}
|
||||
else {
|
||||
Type partType = part.getType();
|
||||
|
||||
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 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());
|
||||
switch (partType) {
|
||||
case IS_NULL:
|
||||
case IS_NOT_NULL:
|
||||
return String.format("%s %s NULL", resolveProperty(alias), resolveOperator(partType));
|
||||
case FALSE:
|
||||
case TRUE:
|
||||
return String.format("%s %s %s", resolveProperty(alias), resolveOperator(partType),
|
||||
Type.TRUE.equals(partType));
|
||||
default:
|
||||
return String.format("%s %s $%d", resolveProperty(alias), resolveOperator(partType),
|
||||
indexes.next());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
boolean isIgnoreCase() {
|
||||
switch (part.shouldIgnoreCase()) {
|
||||
case ALWAYS:
|
||||
case WHEN_POSSIBLE:
|
||||
return true;
|
||||
case NEVER:
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
String resolveProperty(String alias) {
|
||||
return String.format("%1$s.%2$s", resolveAlias(alias), part.getProperty().toDotPath());
|
||||
}
|
||||
|
||||
String resolveAlias(String alias) {
|
||||
return (alias != null ? alias : QueryBuilder.DEFAULT_ALIAS);
|
||||
}
|
||||
|
||||
/**
|
||||
* Maps the given {@link Type} to an OQL operator.
|
||||
*
|
||||
* @param type
|
||||
* @return
|
||||
* Resolves the given {@link Type} as an GemFire OQL operator.
|
||||
*
|
||||
* @param partType the conditional expression (e.g. 'IN') in the query method name.
|
||||
* @return a GemFire OQL operator.
|
||||
*/
|
||||
private String getOperator(Type type) {
|
||||
switch (type) {
|
||||
case IN:
|
||||
return "IN SET";
|
||||
case NOT_IN:
|
||||
return "NOT IN SET";
|
||||
String resolveOperator(Type partType) {
|
||||
switch (partType) {
|
||||
// Equality - Is
|
||||
case FALSE:
|
||||
case IS_NULL:
|
||||
case SIMPLE_PROPERTY:
|
||||
case TRUE:
|
||||
return "=";
|
||||
// Equality - Is Not
|
||||
case IS_NOT_NULL:
|
||||
case NEGATING_SIMPLE_PROPERTY:
|
||||
return "!=";
|
||||
// Relational Comparison
|
||||
case GREATER_THAN:
|
||||
return ">";
|
||||
case GREATER_THAN_EQUAL:
|
||||
@@ -160,26 +188,19 @@ class Predicates implements Predicate {
|
||||
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";
|
||||
*/
|
||||
// Set Containment
|
||||
case IN:
|
||||
return "IN SET";
|
||||
case NOT_IN:
|
||||
return "NOT IN SET";
|
||||
// Wildcard Matching
|
||||
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));
|
||||
throw new IllegalArgumentException(String.format("Unsupported operator %s!", partType));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -56,5 +56,4 @@ class QueryBuilder {
|
||||
public String toString() {
|
||||
return query;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -30,7 +30,7 @@ import com.gemstone.gemfire.cache.Region;
|
||||
|
||||
/**
|
||||
* Value object to work with OQL query strings.
|
||||
*
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
* @author David Turanski
|
||||
* @author John Blum
|
||||
@@ -58,7 +58,7 @@ public class QueryString {
|
||||
|
||||
/**
|
||||
* Creates a {@link QueryString} from the given {@link String} query.
|
||||
*
|
||||
*
|
||||
* @param source a String containing the OQL Query.
|
||||
*/
|
||||
public QueryString(String source) {
|
||||
@@ -68,7 +68,7 @@ public class QueryString {
|
||||
|
||||
/**
|
||||
* Creates a {@literal SELECT} query for the given domain class.
|
||||
*
|
||||
*
|
||||
* @param domainClass must not be {@literal null}.
|
||||
*/
|
||||
@SuppressWarnings("unused")
|
||||
@@ -78,7 +78,7 @@ public class QueryString {
|
||||
|
||||
/**
|
||||
* Creates a {@literal SELECT} query for the given domain class.
|
||||
*
|
||||
*
|
||||
* @param domainClass must not be {@literal null}.
|
||||
* @param isCountQuery indicates if this is a count query
|
||||
*/
|
||||
@@ -118,7 +118,7 @@ public class QueryString {
|
||||
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
@@ -195,5 +195,4 @@ public class QueryString {
|
||||
public String toString() {
|
||||
return query;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -56,7 +56,7 @@ public class StringBasedGemfireRepositoryQuery extends GemfireRepositoryQuery {
|
||||
/**
|
||||
* Creates a new {@link StringBasedGemfireRepositoryQuery} using the given {@link GemfireQueryMethod} and
|
||||
* {@link GemfireTemplate}. The actual query {@link String} will be looked up from the query method.
|
||||
*
|
||||
*
|
||||
* @param queryMethod must not be {@literal null}.
|
||||
* @param template must not be {@literal null}.
|
||||
*/
|
||||
@@ -67,7 +67,7 @@ public class StringBasedGemfireRepositoryQuery extends GemfireRepositoryQuery {
|
||||
/**
|
||||
* Creates a new {@link StringBasedGemfireRepositoryQuery} using the given query {@link String},
|
||||
* {@link GemfireQueryMethod} and {@link GemfireTemplate}.
|
||||
*
|
||||
*
|
||||
* @param query will fall back to the query annotated to the given {@link GemfireQueryMethod} if {@literal null}.
|
||||
* @param queryMethod must not be {@literal null}.
|
||||
* @param template must not be {@literal null}.
|
||||
|
||||
@@ -13,11 +13,14 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.data.gemfire.repository.support;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
import com.gemstone.gemfire.cache.Region;
|
||||
|
||||
import org.springframework.data.gemfire.GemfireTemplate;
|
||||
import org.springframework.data.gemfire.mapping.GemfirePersistentEntity;
|
||||
import org.springframework.data.gemfire.mapping.GemfirePersistentProperty;
|
||||
@@ -33,42 +36,42 @@ import org.springframework.data.repository.core.NamedQueries;
|
||||
import org.springframework.data.repository.core.RepositoryInformation;
|
||||
import org.springframework.data.repository.core.RepositoryMetadata;
|
||||
import org.springframework.data.repository.core.support.RepositoryFactorySupport;
|
||||
import org.springframework.data.repository.query.EvaluationContextProvider;
|
||||
import org.springframework.data.repository.query.QueryLookupStrategy;
|
||||
import org.springframework.data.repository.query.QueryLookupStrategy.Key;
|
||||
import org.springframework.data.repository.query.RepositoryQuery;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import com.gemstone.gemfire.cache.Region;
|
||||
|
||||
/**
|
||||
* {@link RepositoryFactorySupport} implementation creating repository proxies
|
||||
* for Gemfire.
|
||||
*
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
* @author David Turanski
|
||||
* @author John Blum
|
||||
*/
|
||||
public class GemfireRepositoryFactory extends RepositoryFactorySupport {
|
||||
|
||||
private final MappingContext<? extends GemfirePersistentEntity<?>, GemfirePersistentProperty> context;
|
||||
private final MappingContext<? extends GemfirePersistentEntity<?>, GemfirePersistentProperty> mappingContext;
|
||||
|
||||
private final Regions regions;
|
||||
|
||||
/**
|
||||
* Creates a new {@link GemfireRepositoryFactory}.
|
||||
*
|
||||
*
|
||||
* @param regions must not be {@literal null}.
|
||||
* @param context the {@link MappingContext} used by the constructed Repository for mapping entities
|
||||
* @param mappingContext the {@link MappingContext} used by the constructed Repository for mapping entities
|
||||
* to the underlying data store, must not be {@literal null}.
|
||||
*/
|
||||
public GemfireRepositoryFactory(Iterable<Region<?, ?>> regions, MappingContext<? extends GemfirePersistentEntity<?>,
|
||||
GemfirePersistentProperty> context) {
|
||||
|
||||
Assert.notNull(regions, "Regions must not be null!");
|
||||
Assert.notNull(context, "MappingContext must not be null!");
|
||||
|
||||
this.context = context;
|
||||
this.regions = new Regions(regions, this.context);
|
||||
public GemfireRepositoryFactory(Iterable<Region<?, ?>> regions,
|
||||
MappingContext<? extends GemfirePersistentEntity<?>, GemfirePersistentProperty> mappingContext) {
|
||||
|
||||
Assert.notNull(regions, "Regions must not be null");
|
||||
Assert.notNull(mappingContext, "MappingContext must not be null");
|
||||
|
||||
this.mappingContext = mappingContext;
|
||||
this.regions = new Regions(regions, this.mappingContext);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -78,7 +81,7 @@ public class GemfireRepositoryFactory extends RepositoryFactorySupport {
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public <T, ID extends Serializable> GemfireEntityInformation<T, ID> getEntityInformation(Class<T> domainClass) {
|
||||
GemfirePersistentEntity<T> entity = (GemfirePersistentEntity<T>) context.getPersistentEntity(domainClass);
|
||||
GemfirePersistentEntity<T> entity = (GemfirePersistentEntity<T>) mappingContext.getPersistentEntity(domainClass);
|
||||
return new DefaultGemfireEntityInformation<T, ID>(entity);
|
||||
}
|
||||
|
||||
@@ -96,8 +99,8 @@ public class GemfireRepositoryFactory extends RepositoryFactorySupport {
|
||||
return getTargetRepositoryViaReflection(repositoryInformation, gemfireTemplate, entityInformation);
|
||||
}
|
||||
|
||||
private GemfireTemplate getTemplate(RepositoryMetadata metadata) {
|
||||
GemfirePersistentEntity<?> entity = context.getPersistentEntity(metadata.getDomainType());
|
||||
GemfireTemplate getTemplate(RepositoryMetadata metadata) {
|
||||
GemfirePersistentEntity<?> entity = mappingContext.getPersistentEntity(metadata.getDomainType());
|
||||
|
||||
String entityRegionName = entity.getRegionName();
|
||||
String repositoryRegionName = getRepositoryRegionName(metadata.getRepositoryInterface());
|
||||
@@ -106,9 +109,9 @@ public class GemfireRepositoryFactory extends RepositoryFactorySupport {
|
||||
Region<?, ?> region = regions.getRegion(regionName);
|
||||
|
||||
if (region == null) {
|
||||
throw new IllegalStateException(String.format("No Region '%1$s' found for domain class %2$s!"
|
||||
+ " Make sure you have configured a GemFire Region of that name in your application context!",
|
||||
regionName, metadata.getDomainType()));
|
||||
throw new IllegalStateException(String.format("No Region '%1$s' found for domain class %2$s;"
|
||||
+ " Make sure you have configured a GemFire Region of that name in your application context",
|
||||
regionName, metadata.getDomainType().getName()));
|
||||
}
|
||||
|
||||
Class<?> regionKeyType = region.getAttributes().getKeyConstraint();
|
||||
@@ -116,16 +119,16 @@ public class GemfireRepositoryFactory extends RepositoryFactorySupport {
|
||||
|
||||
if (regionKeyType != null && entity.getIdProperty() != null) {
|
||||
Assert.isTrue(regionKeyType.isAssignableFrom(entityIdType), String.format(
|
||||
"The Region referenced only supports keys of type %1$s but the entity to be stored has an id of type %2$s!",
|
||||
regionKeyType, entityIdType));
|
||||
"The Region referenced only supports keys of type %1$s, but the entity to be stored has an id of type %2$s",
|
||||
regionKeyType.getName(), entityIdType.getName()));
|
||||
}
|
||||
|
||||
return new GemfireTemplate(region);
|
||||
}
|
||||
|
||||
private String getRepositoryRegionName(final Class<?> repositoryClass) {
|
||||
return (repositoryClass.isAnnotationPresent(org.springframework.data.gemfire.mapping.Region.class) ?
|
||||
repositoryClass.getAnnotation(org.springframework.data.gemfire.mapping.Region.class).value() : null);
|
||||
String getRepositoryRegionName(Class<?> repositoryInterface) {
|
||||
return (repositoryInterface.isAnnotationPresent(org.springframework.data.gemfire.mapping.Region.class) ?
|
||||
repositoryInterface.getAnnotation(org.springframework.data.gemfire.mapping.Region.class).value() : null);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -142,38 +145,32 @@ public class GemfireRepositoryFactory extends RepositoryFactorySupport {
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see springframework.data.repository.core.support.RepositoryFactorySupport
|
||||
* #getQueryLookupStrategy(org.springframework.data.repository.query.QueryLookupStrategy.Key)
|
||||
* @see org.springframework.data.repository.core.support.RepositoryFactorySupport
|
||||
* #getQueryLookupStrategy(Key, EvaluationContextProvider)
|
||||
*/
|
||||
@Override
|
||||
protected QueryLookupStrategy getQueryLookupStrategy(Key key) {
|
||||
protected QueryLookupStrategy getQueryLookupStrategy(Key key, EvaluationContextProvider evaluationContextProvider) {
|
||||
|
||||
return new QueryLookupStrategy() {
|
||||
|
||||
public RepositoryQuery resolveQuery(Method method, RepositoryMetadata metadata, NamedQueries namedQueries) {
|
||||
return resolveQuery(method, metadata, null, namedQueries);
|
||||
}
|
||||
|
||||
@Override
|
||||
public RepositoryQuery resolveQuery(Method method, RepositoryMetadata metadata, ProjectionFactory factory,
|
||||
NamedQueries namedQueries) {
|
||||
|
||||
GemfireQueryMethod queryMethod = new GemfireQueryMethod(method, metadata, factory, context);
|
||||
|
||||
GemfireQueryMethod queryMethod = new GemfireQueryMethod(method, metadata, factory, mappingContext);
|
||||
GemfireTemplate template = getTemplate(metadata);
|
||||
|
||||
if (queryMethod.hasAnnotatedQuery()) {
|
||||
return new StringBasedGemfireRepositoryQuery(queryMethod, template).asUserDefinedQuery();
|
||||
}
|
||||
|
||||
String namedQueryName = queryMethod.getNamedQueryName();
|
||||
|
||||
if (namedQueries.hasQuery(namedQueryName)) {
|
||||
return new StringBasedGemfireRepositoryQuery(namedQueries.getQuery(namedQueryName), queryMethod,
|
||||
template).asUserDefinedQuery();
|
||||
if (namedQueries.hasQuery(queryMethod.getNamedQueryName())) {
|
||||
return new StringBasedGemfireRepositoryQuery(namedQueries.getQuery(queryMethod.getNamedQueryName()),
|
||||
queryMethod, template).asUserDefinedQuery();
|
||||
}
|
||||
|
||||
return new PartTreeGemfireRepositoryQuery(queryMethod, template);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
*/
|
||||
package org.springframework.data.gemfire.repository.query;
|
||||
|
||||
import static org.hamcrest.CoreMatchers.equalTo;
|
||||
import static org.hamcrest.CoreMatchers.is;
|
||||
import static org.junit.Assert.assertThat;
|
||||
|
||||
@@ -27,8 +28,9 @@ import org.springframework.data.repository.query.parser.PartTree;
|
||||
|
||||
/**
|
||||
* Unit tests for {@link GemfireQueryCreator}.
|
||||
*
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
* @author John Blum
|
||||
*/
|
||||
public class GemfireQueryCreatorUnitTests {
|
||||
|
||||
@@ -37,18 +39,28 @@ public class GemfireQueryCreatorUnitTests {
|
||||
@Before
|
||||
@SuppressWarnings("unchecked")
|
||||
public void setUp() {
|
||||
|
||||
GemfireMappingContext context = new GemfireMappingContext();
|
||||
entity = (GemfirePersistentEntity<Person>) context.getPersistentEntity(Person.class);
|
||||
entity = (GemfirePersistentEntity<Person>) new GemfireMappingContext().getPersistentEntity(Person.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void createsQueryForSimplePropertyReferenceCorrectly() {
|
||||
PartTree partTree = new PartTree("findByLastname", Person.class);
|
||||
|
||||
PartTree partTree = new PartTree("findByFirstname", Person.class);
|
||||
GemfireQueryCreator creator = new GemfireQueryCreator(partTree, entity);
|
||||
GemfireQueryCreator queryCreator = new GemfireQueryCreator(partTree, entity);
|
||||
|
||||
QueryString query = creator.createQuery();
|
||||
assertThat(query.toString(), is("SELECT * FROM /simple x WHERE x.firstname = $1"));
|
||||
QueryString query = queryCreator.createQuery();
|
||||
|
||||
assertThat(query.toString(), is(equalTo("SELECT * FROM /simple x WHERE x.lastname = $1")));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void createsQueryForNestedPropertyReferenceCorrectly() {
|
||||
PartTree partTree = new PartTree("findPersonByAddressCity", Person.class);
|
||||
|
||||
GemfireQueryCreator queryCreator = new GemfireQueryCreator(partTree, entity);
|
||||
|
||||
QueryString query = queryCreator.createQuery();
|
||||
|
||||
assertThat(query.toString(), is(equalTo("SELECT * FROM /simple x WHERE x.address.city = $1")));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,9 +16,12 @@
|
||||
|
||||
package org.springframework.data.gemfire.repository.query;
|
||||
|
||||
import static org.hamcrest.CoreMatchers.*;
|
||||
import static org.junit.Assert.*;
|
||||
import static org.mockito.Mockito.*;
|
||||
import static org.hamcrest.CoreMatchers.equalTo;
|
||||
import static org.hamcrest.CoreMatchers.is;
|
||||
import static org.hamcrest.CoreMatchers.not;
|
||||
import static org.hamcrest.CoreMatchers.nullValue;
|
||||
import static org.junit.Assert.assertThat;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
@@ -47,7 +50,7 @@ import org.springframework.util.ObjectUtils;
|
||||
|
||||
/**
|
||||
* Unit tests for {@link GemfireQueryMethod}.
|
||||
*
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
* @author John Blum
|
||||
*/
|
||||
@@ -58,7 +61,7 @@ public class GemfireQueryMethodUnitTests {
|
||||
public ExpectedException expectedException = ExpectedException.none();
|
||||
|
||||
private GemfireMappingContext context = new GemfireMappingContext();
|
||||
|
||||
|
||||
private ProjectionFactory factory = new SpelAwareProxyProjectionFactory();
|
||||
|
||||
@Mock
|
||||
@@ -115,6 +118,7 @@ public class GemfireQueryMethodUnitTests {
|
||||
}
|
||||
|
||||
@Before
|
||||
@SuppressWarnings("unchecked")
|
||||
public void setup() {
|
||||
when(metadata.getDomainType()).thenReturn((Class) Person.class);
|
||||
when(metadata.getReturnedDomainClass(Mockito.any(Method.class))).thenReturn((Class) Person.class);
|
||||
@@ -146,7 +150,7 @@ public class GemfireQueryMethodUnitTests {
|
||||
public void rejectsQueryMethodWithPageableParameter() throws Exception {
|
||||
expectedException.expect(IllegalStateException.class);
|
||||
expectedException.expectCause(is(nullValue(Throwable.class)));
|
||||
expectedException.expectMessage(Matchers.startsWith("Pagination is not supported by GemFire Repositories!"));
|
||||
expectedException.expectMessage(Matchers.startsWith("Pagination is not supported by GemFire Repositories; Offending method: someMethod"));
|
||||
|
||||
new GemfireQueryMethod(Invalid.class.getMethod("someMethod", Pageable.class), metadata, factory, context);
|
||||
}
|
||||
@@ -280,5 +284,4 @@ public class GemfireQueryMethodUnitTests {
|
||||
void unlimitedQuery();
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -15,8 +15,9 @@
|
||||
*/
|
||||
package org.springframework.data.gemfire.repository.query;
|
||||
|
||||
import static org.hamcrest.CoreMatchers.is;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.hamcrest.Matchers.equalTo;
|
||||
import static org.hamcrest.Matchers.is;
|
||||
import static org.hamcrest.Matchers.notNullValue;
|
||||
import static org.junit.Assert.assertThat;
|
||||
|
||||
import java.util.Arrays;
|
||||
@@ -28,7 +29,8 @@ import org.springframework.data.gemfire.repository.query.Predicates.AtomicPredic
|
||||
import org.springframework.data.repository.query.parser.Part;
|
||||
|
||||
/**
|
||||
*
|
||||
* Unit tests for {@link Predicates}.
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
* @author John Blum
|
||||
*/
|
||||
@@ -37,49 +39,67 @@ public class PredicatesUnitTests {
|
||||
|
||||
@Test
|
||||
public void atomicPredicateDefaultsAlias() {
|
||||
|
||||
Part part = new Part("firstname", Person.class);
|
||||
|
||||
Iterable<Integer> indexes = Arrays.asList(1);
|
||||
Iterator<Integer> indexes = Collections.singletonList(1).iterator();
|
||||
|
||||
Predicate predicate = new AtomicPredicate(part, indexes);
|
||||
|
||||
Predicate predicate = new AtomicPredicate(part, indexes.iterator());
|
||||
assertThat(predicate.toString(null), is("x.firstname = $1"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void concatenatesAndPredicateCorrectly() {
|
||||
|
||||
Part left = new Part("firstname", Person.class);
|
||||
Part right = new Part("lastname", Person.class);
|
||||
|
||||
Iterator<Integer> indexes = Arrays.asList(1, 2).iterator();
|
||||
|
||||
Predicates predicate = Predicates.create(left, indexes);
|
||||
predicate = predicate.and(new AtomicPredicate(right, indexes));
|
||||
Predicate predicate = Predicates.create(left, indexes).and(Predicates.create(right, indexes));
|
||||
|
||||
assertThat(predicate, is(notNullValue(Predicate.class)));
|
||||
assertThat(predicate.toString(null), is("x.firstname = $1 AND x.lastname = $2"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void concatenatesOrPredicateCorrectly() {
|
||||
|
||||
Part left = new Part("firstname", Person.class);
|
||||
Part right = new Part("lastname", Person.class);
|
||||
|
||||
Iterator<Integer> indexes = Arrays.asList(1, 2).iterator();
|
||||
|
||||
Predicates predicate = Predicates.create(left, indexes);
|
||||
predicate = predicate.or(new AtomicPredicate(right, indexes));
|
||||
Predicate predicate = Predicates.create(left, indexes).or(Predicates.create(right, indexes));
|
||||
|
||||
assertThat(predicate.toString(null), is("x.firstname = $1 OR x.lastname = $2"));
|
||||
assertThat(predicate, is(notNullValue(Predicate.class)));
|
||||
assertThat(predicate.toString(null), is(equalTo("x.firstname = $1 OR x.lastname = $2")));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBooleanBasedPredicate() {
|
||||
public void handlesBooleanBasedPredicateCorrectly() {
|
||||
Part part = new Part("activeTrue", User.class);
|
||||
Iterator<Integer> indexes = Collections.<Integer>emptyList().iterator();
|
||||
|
||||
Iterator<Integer> indexes = Collections.singletonList(1).iterator();
|
||||
|
||||
Predicates predicate = Predicates.create(part, indexes);
|
||||
|
||||
assertNotNull(predicate);
|
||||
assertThat(predicate.toString("user"), is("user.active = true"));
|
||||
assertThat(predicate, is(notNullValue(Predicate.class)));
|
||||
assertThat(predicate.toString("user"), is(equalTo("user.active = true")));
|
||||
}
|
||||
|
||||
/**
|
||||
* @link https://jira.spring.io/browse/SGF-507
|
||||
*/
|
||||
@Test
|
||||
public void handlesIgnoreCasePredicateCorrectly() {
|
||||
Part left = new Part("firstnameIgnoreCase", Person.class);
|
||||
Part right = new Part("lastnameIgnoreCase", Person.class);
|
||||
|
||||
Iterator<Integer> indexes = Arrays.asList(1, 2).iterator();
|
||||
|
||||
Predicate predicate = Predicates.create(left, indexes).and(Predicates.create(right, indexes));
|
||||
|
||||
assertThat(predicate, is(notNullValue(Predicate.class)));
|
||||
assertThat(predicate.toString("person"), is(equalTo("person.firstname.equalsIgnoreCase($1) AND person.lastname.equalsIgnoreCase($2)")));
|
||||
}
|
||||
|
||||
static class Person {
|
||||
@@ -87,9 +107,9 @@ public class PredicatesUnitTests {
|
||||
String lastname;
|
||||
}
|
||||
|
||||
// TODO refactor Person to include boolean state; remove User
|
||||
static class User {
|
||||
Boolean active;
|
||||
String username;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -44,7 +44,7 @@ public class IncompatibleRegionKeyEntityIdAnimalRepositoryTest {
|
||||
"IncompatibleRegionKeyEntityIdAnimalRepositoryTest-context.xml");
|
||||
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
public void testStoreAnimalHavingLongIdInRabbitsRegionWithStringKey() {
|
||||
public void storeAnimalHavingLongIdInRabbitsRegionWithStringKey() {
|
||||
try {
|
||||
ConfigurableApplicationContext applicationContext = new ClassPathXmlApplicationContext(
|
||||
APPLICATION_CONTEXT_CONFIG_LOCATION);
|
||||
@@ -62,10 +62,9 @@ public class IncompatibleRegionKeyEntityIdAnimalRepositoryTest {
|
||||
catch (BeanCreationException expected) {
|
||||
//expected.printStackTrace(System.err);
|
||||
assertTrue(expected.getCause() instanceof IllegalArgumentException);
|
||||
assertEquals(String.format("The Region referenced only supports keys of type %1$s but the entity to be stored has an id of type %2$s!",
|
||||
String.class, Long.class), expected.getCause().getMessage());
|
||||
assertEquals(String.format("The Region referenced only supports keys of type %1$s, but the entity to be stored has an id of type %2$s",
|
||||
String.class.getName(), Long.class.getName()), expected.getCause().getMessage());
|
||||
throw (IllegalArgumentException) expected.getCause();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -21,10 +21,11 @@ import java.util.List;
|
||||
import org.springframework.data.domain.Sort;
|
||||
import org.springframework.data.gemfire.repository.GemfireRepository;
|
||||
import org.springframework.data.gemfire.repository.Query;
|
||||
import org.springframework.data.gemfire.repository.query.annotation.Trace;
|
||||
|
||||
/**
|
||||
* Sample Repository interface managing {@link Person}s.
|
||||
*
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
* @author David Turanski
|
||||
* @author John Blum
|
||||
@@ -39,23 +40,26 @@ public interface PersonRepository extends GemfireRepository<Person, Long> {
|
||||
|
||||
Collection<Person> findByFirstname(String firstname);
|
||||
|
||||
Collection<Person> findByFirstnameIn(Collection<String> firstnames);
|
||||
Collection<Person> findByFirstnameContaining(String firstName);
|
||||
|
||||
Collection<Person> findByFirstnameIn(String... firstnames);
|
||||
Collection<Person> findByFirstnameIn(Collection<String> firstNames);
|
||||
|
||||
Collection<Person> findByFirstnameAndLastname(String firstname, String lastname);
|
||||
Collection<Person> findByFirstnameIn(String... firstNames);
|
||||
|
||||
Collection<Person> findByFirstnameOrLastname(String firstname, String lastname);
|
||||
Collection<Person> findByFirstnameLike(String firstName);
|
||||
|
||||
Person findByLastname(String lastname);
|
||||
Collection<Person> findByFirstnameStartingWith(String firstName);
|
||||
|
||||
Collection<Person> findByFirstnameLike(String firstname);
|
||||
Collection<Person> findByFirstnameAndLastname(String firstName, String lastName);
|
||||
|
||||
Collection<Person> findByFirstnameStartingWith(String firstname);
|
||||
@Trace
|
||||
Collection<Person> findByFirstnameIgnoreCaseAndLastnameIgnoreCase(String firstName, String lastName);
|
||||
|
||||
Collection<Person> findByLastnameEndingWith(String lastname);
|
||||
Collection<Person> findByFirstnameOrLastname(String firstName, String lastName);
|
||||
|
||||
Collection<Person> findByFirstnameContaining(String firstname);
|
||||
Person findByLastname(String lastName);
|
||||
|
||||
Collection<Person> findByLastnameEndingWith(String lastName);
|
||||
|
||||
List<Person> findDistinctByLastname(String lastName, Sort order);
|
||||
|
||||
|
||||
@@ -16,19 +16,34 @@
|
||||
|
||||
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.hamcrest.Matchers.contains;
|
||||
import static org.hamcrest.Matchers.equalTo;
|
||||
import static org.hamcrest.Matchers.is;
|
||||
import static org.hamcrest.Matchers.notNullValue;
|
||||
import static org.junit.Assert.assertThat;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Properties;
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
|
||||
import com.gemstone.gemfire.cache.Cache;
|
||||
import com.gemstone.gemfire.cache.RegionAttributes;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.ComponentScan;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.FilterType;
|
||||
import org.springframework.data.domain.Sort;
|
||||
import org.springframework.data.gemfire.CacheFactoryBean;
|
||||
import org.springframework.data.gemfire.LocalRegionFactoryBean;
|
||||
import org.springframework.data.gemfire.RegionAttributesFactoryBean;
|
||||
import org.springframework.data.gemfire.repository.config.EnableGemfireRepositories;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
|
||||
@@ -44,75 +59,142 @@ import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
* @see org.springframework.test.context.junit4.SpringJUnit4ClassRunner
|
||||
* @since 1.4.0
|
||||
*/
|
||||
@ContextConfiguration
|
||||
@RunWith(SpringJUnit4ClassRunner.class)
|
||||
@ContextConfiguration(classes = PersonRepositoryTest.GemFireConfiguration.class)
|
||||
@SuppressWarnings("unused")
|
||||
public class PersonRepositoryTest {
|
||||
|
||||
private static final String GEMFIRE_LOG_LEVEL = System.getProperty("gemfire.log-level", "warning");
|
||||
|
||||
protected final AtomicLong ID_SEQUENCE = new AtomicLong(0l);
|
||||
|
||||
private Person cookieDoe = createPerson("Cookie", "Doe");
|
||||
private Person janeDoe = createPerson("Jane", "Doe");
|
||||
private Person jonDoe = createPerson("Jon", "Doe");
|
||||
private Person pieDoe = createPerson("Pie", "Doe");
|
||||
private Person jackHandy = createPerson("Jack", "Handy");
|
||||
private Person sandyHandy = createPerson("Sandy", "Handy");
|
||||
private Person imaPigg = createPerson("Ima", "Pigg");
|
||||
private Person cookieDoe = newPerson("Cookie", "Doe");
|
||||
private Person janeDoe = newPerson("Jane", "Doe");
|
||||
private Person jonDoe = newPerson("Jon", "Doe");
|
||||
private Person pieDoe = newPerson("Pie", "Doe");
|
||||
private Person jackHandy = newPerson("Jack", "Handy");
|
||||
private Person sandyHandy = newPerson("Sandy", "Handy");
|
||||
private Person imaPigg = newPerson("Ima", "Pigg");
|
||||
|
||||
@Autowired
|
||||
private PersonRepository personRepo;
|
||||
private PersonRepository personRepository;
|
||||
|
||||
@Before
|
||||
public void setup() {
|
||||
if (personRepo.count() == 0) {
|
||||
sandyHandy = personRepo.save(sandyHandy);
|
||||
jonDoe = personRepo.save(jonDoe);
|
||||
jackHandy = personRepo.save(jackHandy);
|
||||
janeDoe = personRepo.save(janeDoe);
|
||||
pieDoe = personRepo.save(pieDoe);
|
||||
imaPigg = personRepo.save(imaPigg);
|
||||
cookieDoe = personRepo.save(cookieDoe);
|
||||
if (personRepository.count() == 0) {
|
||||
sandyHandy = personRepository.save(sandyHandy);
|
||||
jonDoe = personRepository.save(jonDoe);
|
||||
jackHandy = personRepository.save(jackHandy);
|
||||
janeDoe = personRepository.save(janeDoe);
|
||||
pieDoe = personRepository.save(pieDoe);
|
||||
imaPigg = personRepository.save(imaPigg);
|
||||
cookieDoe = personRepository.save(cookieDoe);
|
||||
}
|
||||
|
||||
assertEquals(7l, personRepo.count());
|
||||
assertThat(personRepository.count(), is(equalTo(7L)));
|
||||
}
|
||||
|
||||
protected Person createPerson(final String firstName, final String lastName) {
|
||||
protected Person newPerson(String firstName, String lastName) {
|
||||
return new Person(ID_SEQUENCE.incrementAndGet(), firstName, lastName);
|
||||
}
|
||||
|
||||
protected Sort.Order createOrder(final String property) {
|
||||
return createOrder(property, Sort.Direction.ASC);
|
||||
protected Sort.Order newOrder(String property) {
|
||||
return newOrder(property, Sort.Direction.ASC);
|
||||
}
|
||||
|
||||
protected Sort.Order createOrder(final String property, final Sort.Direction direction) {
|
||||
protected Sort.Order newOrder(String property, Sort.Direction direction) {
|
||||
return new Sort.Order(direction, property);
|
||||
}
|
||||
|
||||
protected Sort createSort(final Sort.Order... orders) {
|
||||
protected Sort newSort(Sort.Order... orders) {
|
||||
return new Sort(orders);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFindDistinctPeopleWithOrder() {
|
||||
List<Person> actualPeople = personRepo.findDistinctPeopleByOrderByLastnameDesc(
|
||||
createSort(createOrder("firstname")));
|
||||
public void findDistinctPeopleOrderedByFirstnameDescending() {
|
||||
List<Person> actualPeople = personRepository.findDistinctPeopleByOrderByLastnameDesc(
|
||||
newSort(newOrder("firstname")));
|
||||
|
||||
assertNotNull(actualPeople);
|
||||
assertFalse(actualPeople.isEmpty());
|
||||
assertEquals(7, actualPeople.size());
|
||||
assertEquals(Arrays.asList(imaPigg, jackHandy, sandyHandy, cookieDoe, janeDoe, jonDoe, pieDoe), actualPeople);
|
||||
assertThat(actualPeople, is(notNullValue(List.class)));
|
||||
assertThat(actualPeople.size(), is(equalTo(7)));
|
||||
assertThat(actualPeople, is(equalTo(Arrays.asList(
|
||||
imaPigg, jackHandy, sandyHandy, cookieDoe, janeDoe, jonDoe, pieDoe))));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFindDistinctPersonWithNoOrder() {
|
||||
List<Person> actualPeople = personRepo.findDistinctByLastname("Pigg", null);
|
||||
public void findDistinctPersonWithUnordered() {
|
||||
List<Person> actualPeople = personRepository.findDistinctByLastname("Handy", null);
|
||||
|
||||
assertNotNull(actualPeople);
|
||||
assertFalse(actualPeople.isEmpty());
|
||||
assertEquals(1, actualPeople.size());
|
||||
assertEquals(String.format("Expected '%1$s'; but was '%2$s'", imaPigg, actualPeople.get(0)),
|
||||
imaPigg, actualPeople.get(0));
|
||||
assertThat(actualPeople, is(notNullValue(List.class)));
|
||||
assertThat(actualPeople.size(), is(equalTo(2)));
|
||||
assertThat(String.format("Expected '%1$s'; but was '%2$s'", Arrays.asList(jackHandy, sandyHandy), actualPeople),
|
||||
actualPeople, contains(jackHandy, sandyHandy));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void findPersonByFirstAndLastNameIgnoringCase() {
|
||||
Collection<Person> people = personRepository.findByFirstnameIgnoreCaseAndLastnameIgnoreCase("jON", "doE");
|
||||
|
||||
assertThat(people, is(notNullValue(Collection.class)));
|
||||
assertThat(people.size(), is(equalTo(1)));
|
||||
assertThat(people.iterator().next(), is(equalTo(jonDoe)));
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@EnableGemfireRepositories(basePackages = "org.springframework.data.gemfire.repository.sample",
|
||||
includeFilters = @ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE,
|
||||
value = org.springframework.data.gemfire.repository.sample.PersonRepository.class))
|
||||
public static class GemFireConfiguration {
|
||||
|
||||
String applicationName() {
|
||||
return PersonRepositoryTest.class.getSimpleName();
|
||||
}
|
||||
|
||||
String logLevel() {
|
||||
return GEMFIRE_LOG_LEVEL;
|
||||
}
|
||||
|
||||
Properties gemfireProperties() {
|
||||
Properties gemfireProperties = new Properties();
|
||||
|
||||
gemfireProperties.setProperty("name", applicationName());
|
||||
gemfireProperties.setProperty("mcast-port", "0");
|
||||
gemfireProperties.setProperty("log-level", logLevel());
|
||||
|
||||
return gemfireProperties;
|
||||
}
|
||||
|
||||
@Bean
|
||||
CacheFactoryBean gemfireCache() {
|
||||
CacheFactoryBean gemfireCache = new CacheFactoryBean();
|
||||
|
||||
gemfireCache.setClose(true);
|
||||
gemfireCache.setProperties(gemfireProperties());
|
||||
|
||||
return gemfireCache;
|
||||
}
|
||||
|
||||
@Bean(name = "simple")
|
||||
LocalRegionFactoryBean simpleRegion(Cache gemfireCache, RegionAttributes<Long, Person> simpleRegionAttributes) {
|
||||
LocalRegionFactoryBean<Long, Person> simpleRegion = new LocalRegionFactoryBean<Long, Person>();
|
||||
|
||||
simpleRegion.setAttributes(simpleRegionAttributes);
|
||||
simpleRegion.setCache(gemfireCache);
|
||||
simpleRegion.setClose(false);
|
||||
simpleRegion.setPersistent(false);
|
||||
|
||||
return simpleRegion;
|
||||
}
|
||||
|
||||
@Bean
|
||||
@SuppressWarnings("unchecked")
|
||||
RegionAttributesFactoryBean simpleRegionAttributes() {
|
||||
RegionAttributesFactoryBean simpleRegionAttributes = new RegionAttributesFactoryBean();
|
||||
|
||||
simpleRegionAttributes.setKeyConstraint(Long.class);
|
||||
simpleRegionAttributes.setValueConstraint(Person.class);
|
||||
|
||||
return simpleRegionAttributes;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -43,7 +43,7 @@ public class PlantRepositoryTest {
|
||||
"PlantRepositoryTest-context.xml");
|
||||
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
public void testStorePlantHavingStringIdInPlantsRegionWithLongKey() {
|
||||
public void storePlantHavingStringIdInPlantsRegionWithLongKey() {
|
||||
try {
|
||||
ConfigurableApplicationContext context = new ClassPathXmlApplicationContext(
|
||||
APPLICATION_CONTEXT_CONFIG_LOCATION);
|
||||
@@ -54,10 +54,9 @@ public class PlantRepositoryTest {
|
||||
catch (BeanCreationException expected) {
|
||||
//expected.printStackTrace(System.err);
|
||||
assertTrue(expected.getCause() instanceof IllegalArgumentException);
|
||||
assertEquals(String.format("The Region referenced only supports keys of type %1$s but the entity to be stored has an id of type %2$s!",
|
||||
Long.class, String.class), expected.getCause().getMessage());
|
||||
assertEquals(String.format("The Region referenced only supports keys of type %1$s, but the entity to be stored has an id of type %2$s",
|
||||
Long.class.getName(), String.class.getName()), expected.getCause().getMessage());
|
||||
throw (IllegalArgumentException) expected.getCause();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -13,35 +13,46 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.data.gemfire.repository.support;
|
||||
|
||||
import static org.junit.Assert.assertSame;
|
||||
import static org.hamcrest.Matchers.equalTo;
|
||||
import static org.hamcrest.Matchers.is;
|
||||
import static org.hamcrest.Matchers.notNullValue;
|
||||
import static org.hamcrest.Matchers.nullValue;
|
||||
import static org.hamcrest.Matchers.startsWith;
|
||||
import static org.junit.Assert.assertThat;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.never;
|
||||
import static org.mockito.Mockito.times;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.verifyZeroInteractions;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
|
||||
import org.hamcrest.Matchers;
|
||||
import com.gemstone.gemfire.cache.Region;
|
||||
import com.gemstone.gemfire.cache.RegionAttributes;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.rules.ExpectedException;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.invocation.InvocationOnMock;
|
||||
import org.mockito.runners.MockitoJUnitRunner;
|
||||
import org.mockito.stubbing.Answer;
|
||||
import org.springframework.aop.framework.Advised;
|
||||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.data.domain.Pageable;
|
||||
import org.springframework.data.domain.Sort;
|
||||
import org.springframework.data.gemfire.GemfireTemplate;
|
||||
import org.springframework.data.gemfire.mapping.GemfireMappingContext;
|
||||
import org.springframework.data.gemfire.repository.GemfireRepository;
|
||||
import org.springframework.data.gemfire.repository.sample.Person;
|
||||
import org.springframework.data.repository.PagingAndSortingRepository;
|
||||
import org.springframework.data.repository.Repository;
|
||||
import org.springframework.data.repository.core.EntityInformation;
|
||||
|
||||
import com.gemstone.gemfire.cache.Region;
|
||||
import com.gemstone.gemfire.cache.RegionAttributes;
|
||||
import org.springframework.data.repository.core.RepositoryMetadata;
|
||||
|
||||
/**
|
||||
* Unit tests for {@link GemfireRepositoryFactory}.
|
||||
@@ -54,80 +65,235 @@ import com.gemstone.gemfire.cache.RegionAttributes;
|
||||
@SuppressWarnings("unused")
|
||||
public class GemfireRepositoryFactoryUnitTests {
|
||||
|
||||
@Rule
|
||||
public ExpectedException exception = ExpectedException.none();
|
||||
|
||||
private GemfireMappingContext gemfireMappingContext = new GemfireMappingContext();
|
||||
|
||||
@Mock
|
||||
private Region<?, ?> region;
|
||||
private Region<Object, Object> mockRegion;
|
||||
|
||||
@Mock
|
||||
@SuppressWarnings("rawtypes")
|
||||
private RegionAttributes attributes;
|
||||
private RegionAttributes mockRegionAttributes;
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
protected <K, V> Region<K, V> configureMockRegion(Region<K, V> mockRegion, String name,
|
||||
Class<K> keyType, Class<V> valueType) {
|
||||
|
||||
when(mockRegion.getAttributes()).thenReturn(mockRegionAttributes);
|
||||
when(mockRegion.getFullPath()).thenReturn(String.format("%1$s%2$s", Region.SEPARATOR, name));
|
||||
when(mockRegion.getName()).thenReturn(name);
|
||||
when(mockRegionAttributes.getKeyConstraint()).thenReturn(keyType);
|
||||
when(mockRegionAttributes.getValueConstraint()).thenReturn(valueType);
|
||||
|
||||
return mockRegion;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
protected <K, V> Region<K, V> mockRegion(String name, Class<K> keyType, Class<V> valueType) {
|
||||
return configureMockRegion(mock(Region.class, name), name, keyType, valueType);
|
||||
}
|
||||
|
||||
protected RepositoryMetadata mockRepositoryMetadata(final Class<?> domainType, final Class<?> idType,
|
||||
final Class<?> repositoryInterface) {
|
||||
|
||||
RepositoryMetadata mockRepositoryMetadata = mock(RepositoryMetadata.class);
|
||||
|
||||
when(mockRepositoryMetadata.getDomainType()).then(new Answer<Class<?>>() {
|
||||
@Override public Class<?> answer(InvocationOnMock invocation) throws Throwable {
|
||||
return domainType;
|
||||
}
|
||||
});
|
||||
|
||||
when(mockRepositoryMetadata.getIdType()).then(new Answer<Class<?>>() {
|
||||
@Override public Class<?> answer(InvocationOnMock invocation) throws Throwable {
|
||||
return idType;
|
||||
}
|
||||
});
|
||||
|
||||
when(mockRepositoryMetadata.getRepositoryInterface()).then(new Answer<Class<?>>() {
|
||||
@Override public Class<?> answer(InvocationOnMock invocation) throws Throwable {
|
||||
return repositoryInterface;
|
||||
}
|
||||
});
|
||||
|
||||
return mockRepositoryMetadata;
|
||||
}
|
||||
|
||||
@Before
|
||||
@SuppressWarnings("unchecked")
|
||||
public void setup() {
|
||||
when(region.getName()).thenReturn("simple");
|
||||
when(region.getFullPath()).thenReturn("/simple");
|
||||
when(region.getAttributes()).thenReturn(attributes);
|
||||
configureMockRegion(mockRegion, "simple", Object.class, Object.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void constructGemfireRepositoryFactoryWithNullMappingContextThrowsIllegalArgumentException() {
|
||||
exception.expect(IllegalArgumentException.class);
|
||||
exception.expectCause(is(nullValue(Throwable.class)));
|
||||
exception.expectMessage("MappingContext must not be null");
|
||||
|
||||
new GemfireRepositoryFactory(Collections.<Region<?, ?>>emptyList(), null);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void constructGemfireRepositoryFactoryWithNullRegionsThrowsIllegalArgumentException() {
|
||||
exception.expect(IllegalArgumentException.class);
|
||||
exception.expectCause(is(nullValue(Throwable.class)));
|
||||
exception.expectMessage("Regions must not be null");
|
||||
|
||||
new GemfireRepositoryFactory(null, gemfireMappingContext);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getRepositoryRegionNameFromRepositoryInterfaceWithRegionAnnotation() {
|
||||
GemfireRepositoryFactory gemfireRepositoryFactory = new GemfireRepositoryFactory(
|
||||
Collections.<Region<?, ?>>emptyList(), gemfireMappingContext);
|
||||
|
||||
assertThat(gemfireRepositoryFactory.getRepositoryRegionName(PersonRepository.class),
|
||||
is(equalTo("People")));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getRepositoryRegionNameFromRepositoryInterfaceWithoutRegionAnnotation() {
|
||||
GemfireRepositoryFactory gemfireRepositoryFactory = new GemfireRepositoryFactory(
|
||||
Collections.<Region<?, ?>>emptyList(), gemfireMappingContext);
|
||||
|
||||
assertThat(gemfireRepositoryFactory.getRepositoryRegionName(SampleCustomGemfireRepository.class),
|
||||
is(nullValue(String.class)));
|
||||
}
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("unchecked")
|
||||
public void getTemplateReturnsGemfireTemplateForPeopleRegion() {
|
||||
RepositoryMetadata mockRepositoryMetadata = mockRepositoryMetadata(Person.class, Long.class,
|
||||
PersonRepository.class);
|
||||
|
||||
Region<Long, Person> mockPeopleRegion = mockRegion("People", Long.class, Person.class);
|
||||
|
||||
Iterable<Region<?, ?>> regions = Arrays.asList(mockRegion, mockPeopleRegion);
|
||||
|
||||
GemfireRepositoryFactory gemfireRepositoryFactory = new GemfireRepositoryFactory(
|
||||
regions, gemfireMappingContext);
|
||||
|
||||
GemfireTemplate gemfireTemplate = gemfireRepositoryFactory.getTemplate(mockRepositoryMetadata);
|
||||
|
||||
assertThat(gemfireTemplate, is(notNullValue(GemfireTemplate.class)));
|
||||
assertThat(gemfireTemplate.<Long, Person>getRegion(), is(equalTo(mockPeopleRegion)));
|
||||
|
||||
verify(mockPeopleRegion, times(1)).getAttributes();
|
||||
verify(mockPeopleRegion, times(1)).getFullPath();
|
||||
verify(mockPeopleRegion, times(1)).getName();
|
||||
verify(mockRegionAttributes, times(1)).getKeyConstraint();
|
||||
verify(mockRepositoryMetadata, times(1)).getDomainType();
|
||||
verify(mockRepositoryMetadata, times(1)).getIdType();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getTemplateReturnsGemfireTemplateForSimpleRegion() {
|
||||
RepositoryMetadata mockRepositoryMetadata = mockRepositoryMetadata(Person.class, Long.class,
|
||||
SampleCustomGemfireRepository.class);
|
||||
|
||||
Iterable<Region<?, ?>> regions = Collections.<Region<?, ?>>singleton(mockRegion);
|
||||
|
||||
GemfireRepositoryFactory gemfireRepositoryFactory = new GemfireRepositoryFactory(
|
||||
regions, gemfireMappingContext);
|
||||
|
||||
GemfireTemplate gemfireTemplate = gemfireRepositoryFactory.getTemplate(mockRepositoryMetadata);
|
||||
|
||||
assertThat(gemfireTemplate, is(notNullValue(GemfireTemplate.class)));
|
||||
assertThat(gemfireTemplate.getRegion(), is(equalTo(mockRegion)));
|
||||
|
||||
verify(mockRepositoryMetadata, times(1)).getDomainType();
|
||||
verify(mockRepositoryMetadata, times(1)).getIdType();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getTemplateThrowsIllegalArgumentExceptionForIncompatibleRegionKeyTypeAndRepositoryIdType() {
|
||||
RepositoryMetadata mockRepositoryMetadata = mockRepositoryMetadata(Person.class, Long.class,
|
||||
PersonRepository.class);
|
||||
|
||||
Region<String, Person> mockPeopleRegion = mockRegion("People", String.class, Person.class);
|
||||
|
||||
GemfireRepositoryFactory gemfireRepositoryFactory = new GemfireRepositoryFactory(
|
||||
Collections.<Region<?, ?>>singleton(mockPeopleRegion), gemfireMappingContext);
|
||||
|
||||
try {
|
||||
exception.expect(IllegalArgumentException.class);
|
||||
exception.expectCause(is(nullValue(Throwable.class)));
|
||||
exception.expectMessage(String.format(
|
||||
"The Region referenced only supports keys of type %1$s, but the entity to be stored has an id of type %2$s",
|
||||
String.class.getName(), Long.class.getName()));
|
||||
|
||||
gemfireRepositoryFactory.getTemplate(mockRepositoryMetadata);
|
||||
}
|
||||
finally {
|
||||
verify(mockRepositoryMetadata, times(1)).getDomainType();
|
||||
verify(mockRepositoryMetadata, times(1)).getIdType();
|
||||
verify(mockPeopleRegion, times(1)).getAttributes();
|
||||
verify(mockPeopleRegion, times(1)).getFullPath();
|
||||
verify(mockPeopleRegion, times(1)).getName();
|
||||
verify(mockRegionAttributes, times(1)).getKeyConstraint();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getTemplateThrowsIllegalStateExceptionForRegionNotFound() {
|
||||
RepositoryMetadata mockRepositoryMetadata = mockRepositoryMetadata(Person.class, Long.class,
|
||||
PersonRepository.class);
|
||||
|
||||
GemfireRepositoryFactory gemfireRepositoryFactory = new GemfireRepositoryFactory(
|
||||
Collections.<Region<?, ?>>singleton(mockRegion), gemfireMappingContext);
|
||||
|
||||
try {
|
||||
exception.expect(IllegalStateException.class);
|
||||
exception.expectCause(is(nullValue(Throwable.class)));
|
||||
exception.expectMessage(String.format(
|
||||
"No Region 'People' found for domain class %s; Make sure you have configured a GemFire Region of that name in your application context",
|
||||
Person.class.getName()));
|
||||
|
||||
gemfireRepositoryFactory.getTemplate(mockRepositoryMetadata);
|
||||
}
|
||||
finally {
|
||||
verify(mockRepositoryMetadata, times(2)).getDomainType();
|
||||
verify(mockRepositoryMetadata, never()).getIdType();
|
||||
verify(mockRegion, times(1)).getFullPath();
|
||||
verify(mockRegion, times(1)).getName();
|
||||
verifyZeroInteractions(mockRegionAttributes);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @link https://jira.spring.io/browse/SGF-112
|
||||
*/
|
||||
@Test(expected = IllegalStateException.class)
|
||||
@Test
|
||||
public void rejectsInterfacesExtendingPagingAndSortingRepository() {
|
||||
GemfireRepositoryFactory repositoryFactory = new GemfireRepositoryFactory(
|
||||
Collections.<Region<?, ?>>singletonList(region), new GemfireMappingContext());
|
||||
exception.expect(IllegalStateException.class);
|
||||
exception.expectCause(is(nullValue(Throwable.class)));
|
||||
exception.expectMessage(startsWith("Pagination is not supported by GemFire Repositories"));
|
||||
|
||||
try {
|
||||
repositoryFactory.getRepository(SamplePagingAndSortingRepository.class);
|
||||
//factory.getRepository(SamplePagingRepository.class);
|
||||
//factory.getRepository(SampleSortingRepository.class);
|
||||
}
|
||||
catch (IllegalStateException expected) {
|
||||
assertThat(expected.getMessage(), Matchers.startsWith(
|
||||
"Pagination is not supported by GemFire Repositories!"));
|
||||
throw expected;
|
||||
}
|
||||
GemfireRepositoryFactory repositoryFactory = new GemfireRepositoryFactory(
|
||||
Collections.<Region<?, ?>>singletonList(mockRegion), new GemfireMappingContext());
|
||||
|
||||
repositoryFactory.getRepository(SamplePagingAndSortingRepository.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void usesConfiguredRepositoryBaseClass() {
|
||||
GemfireRepositoryFactory repositoryFactory = new GemfireRepositoryFactory(
|
||||
Collections.<Region<?, ?>>singletonList(region), new GemfireMappingContext());
|
||||
Collections.<Region<?, ?>>singletonList(mockRegion), new GemfireMappingContext());
|
||||
|
||||
repositoryFactory.setRepositoryBaseClass(CustomBaseRepository.class);
|
||||
|
||||
GemfireRepository<?, ?> gemfireRepository = repositoryFactory.getRepository(SampleCustomGemfireRepository.class,
|
||||
new SampleCustomRepositoryImpl());
|
||||
|
||||
assertSame(CustomBaseRepository.class, ((Advised) gemfireRepository).getTargetClass());
|
||||
assertThat(((Advised) gemfireRepository).getTargetClass(), is(equalTo((Class) CustomBaseRepository.class)));
|
||||
}
|
||||
|
||||
interface SamplePagingAndSortingRepository extends PagingAndSortingRepository<Person, Long> {
|
||||
}
|
||||
|
||||
interface SamplePagingRepository extends Repository<Person, Long> {
|
||||
Page<Person> findAll(Pageable pageable);
|
||||
}
|
||||
|
||||
interface SampleSortingRepository extends Repository<Person, Long> {
|
||||
Iterable<Person> findAll(Sort sort);
|
||||
}
|
||||
|
||||
interface SampleCustomRepository<T> {
|
||||
void doCustomUpdate(T entity);
|
||||
}
|
||||
|
||||
class SampleCustomRepositoryImpl<T> implements SampleCustomRepository<T> {
|
||||
|
||||
@Override
|
||||
public void doCustomUpdate(final T entity) {
|
||||
throw new UnsupportedOperationException("Not Implemented!");
|
||||
}
|
||||
}
|
||||
|
||||
interface SampleCustomGemfireRepository extends GemfireRepository<Person, Long>, SampleCustomRepository<Person> {
|
||||
}
|
||||
|
||||
static class CustomBaseRepository<T, ID extends Serializable> extends SimpleGemfireRepository<T, ID> {
|
||||
|
||||
public CustomBaseRepository(GemfireTemplate template, EntityInformation<T, ID> entityInformation) {
|
||||
@@ -135,4 +301,21 @@ public class GemfireRepositoryFactoryUnitTests {
|
||||
}
|
||||
}
|
||||
|
||||
interface SampleCustomRepository<T> {
|
||||
void doCustomUpdate(T entity);
|
||||
}
|
||||
|
||||
class SampleCustomRepositoryImpl<T> implements SampleCustomRepository<T> {
|
||||
@Override
|
||||
public void doCustomUpdate(final T entity) {
|
||||
throw new UnsupportedOperationException("Not Implemented");
|
||||
}
|
||||
}
|
||||
|
||||
interface SampleCustomGemfireRepository extends GemfireRepository<Person, Long>, SampleCustomRepository<Person> {
|
||||
}
|
||||
|
||||
@org.springframework.data.gemfire.mapping.Region("People")
|
||||
interface PersonRepository extends GemfireRepository<Person, Long> {
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,31 +0,0 @@
|
||||
<?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:repo="http://www.springframework.org/schema/data/repository"
|
||||
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/data/repository http://www.springframework.org/schema/data/repository/spring-repository.xsd
|
||||
http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd
|
||||
">
|
||||
|
||||
<util:properties id="gemfireProperties">
|
||||
<prop key="name">springGemFirePersonRepositoryTest</prop>
|
||||
<prop key="mcast-port">0</prop>
|
||||
<prop key="log-level">warning</prop>
|
||||
</util:properties>
|
||||
|
||||
<gfe:cache properties-ref="gemfireProperties"/>
|
||||
|
||||
<gfe:replicated-region id="simple" persistent="false" key-constraint="java.lang.Long"
|
||||
value-constraint="org.springframework.data.gemfire.repository.sample.Person"/>
|
||||
|
||||
<gfe-data:repositories base-package="org.springframework.data.gemfire.repository.sample">
|
||||
<repo:include-filter type="assignable" expression="org.springframework.data.gemfire.repository.sample.PersonRepository"/>
|
||||
</gfe-data:repositories>
|
||||
|
||||
</beans>
|
||||
Reference in New Issue
Block a user