SGF-392 - Add support for OQL Query statement extensions in Repository Query methods via Annotations.

Implementation and unit test coverage complete.
(cherry picked from commit 23d05aeaee743922a9d371b8b7f8201e6cb9578c)

Signed-off-by: John Blum <jblum@pivotal.io>
This commit is contained in:
John Blum
2015-08-11 23:08:25 -07:00
parent 780ce89050
commit 5d8a1d2ce5
12 changed files with 500 additions and 56 deletions

View File

@@ -26,8 +26,7 @@ import org.springframework.data.util.TypeInformation;
*
* @author Oliver Gierke
*/
public class GemfireMappingContext extends
AbstractMappingContext<GemfirePersistentEntity<?>, GemfirePersistentProperty> {
public class GemfireMappingContext extends AbstractMappingContext<GemfirePersistentEntity<?>, GemfirePersistentProperty> {
/*
* (non-Javadoc)
@@ -47,4 +46,5 @@ public class GemfireMappingContext extends
GemfirePersistentEntity<?> owner, SimpleTypeHolder simpleTypeHolder) {
return new GemfirePersistentProperty(field, descriptor, owner, simpleTypeHolder);
}
}

View File

@@ -75,8 +75,9 @@ public class Regions implements Iterable<Region<?, ?>> {
Assert.notNull(type);
GemfirePersistentEntity<?> entity = context.getPersistentEntity(type);
String regionName = (entity != null ? entity.getRegionName() : type.getSimpleName());
return (Region<?, T>) (entity == null ? regions.get(type.getSimpleName()) : regions.get(entity.getRegionName()));
return (Region<?, T>) regions.get(regionName);
}
/**

View File

@@ -13,6 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.gemfire.repository.query;
import java.lang.reflect.Method;
@@ -33,9 +34,11 @@ import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
/**
* Gemfire specific {@link QueryMethod}.
* GemFire specific {@link QueryMethod}.
*
* @author Oliver Gierke
* @author John Blum
* @see org.springframework.data.repository.query.QueryMethod
*/
@SuppressWarnings("unused")
public class GemfireQueryMethod extends QueryMethod {
@@ -76,8 +79,8 @@ public class GemfireQueryMethod extends QueryMethod {
private void assertNonPagingQueryMethod(final Method method) {
for (Class<?> type : method.getParameterTypes()) {
if (Pageable.class.isAssignableFrom(type)) {
throw new IllegalStateException("Pagination is not supported by GemFire Repositories! Offending method: "
+ method.toString());
throw new IllegalStateException(String.format("Pagination is not supported by GemFire Repositories!"
+ " Offending method: %1$s", method.toString()));
}
}
}

View File

@@ -34,12 +34,12 @@ class QueryBuilder {
private final String query;
public QueryBuilder(String source) {
Assert.hasText(source, "The OQL Query string must be specified.");
Assert.hasText(source, "The OQL Query must be specified");
this.query = source;
}
public QueryBuilder(GemfirePersistentEntity<?> entity, PartTree tree) {
this(String.format(tree.isDistinct() ? "SELECT DISTINCT * FROM /%1$s %2$s" : "SELECT * FROM /%1$s %2$s",
this(String.format("SELECT%1$s * FROM /%2$s %3$s", (tree.isDistinct() ? " DISTINCT" : ""),
entity.getRegionName(), DEFAULT_ALIAS));
}

View File

@@ -23,6 +23,7 @@ import java.util.regex.Pattern;
import org.springframework.data.domain.Sort;
import org.springframework.util.Assert;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;
import com.gemstone.gemfire.cache.Region;
@@ -36,6 +37,19 @@ import com.gemstone.gemfire.cache.Region;
*/
public class QueryString {
protected static final Pattern HINT_PATTERN = Pattern.compile("<HINT '\\w+'(, '\\w+')*>");
protected static final Pattern IMPORT_PATTERN = Pattern.compile("IMPORT .+;");
protected static final Pattern LIMIT_PATTERN = Pattern.compile("LIMIT \\d+");
protected static final Pattern TRACE_PATTERN = Pattern.compile("<TRACE>");
// OQL Query Templates
private static final String HINTS_QUERY_TEMPLATE = "<HINT %1$s> %2$s";
private static final String IMPORT_QUERY_TEMPLATE = "IMPORT %1$s; %2$s";
private static final String LIMIT_QUERY_TEMPLATE = "%1$s LIMIT %2$d";
private static final String SELECT_QUERY_TEMPLATE = "SELECT %1$s FROM /%2$s";
private static final String TRACE_QUERY_TEMPLATE = "<TRACE> %1$s";
// Query Regular Expression Patterns
private static final String IN_PATTERN = "(?<=IN (SET|LIST) )\\$\\d";
private static final String IN_PARAMETER_PATTERN = "(?<=IN (SET|LIST) \\$)\\d";
private static final String REGION_PATTERN = "\\/(\\/?\\w)+";
@@ -69,8 +83,7 @@ public 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()));
this(String.format(SELECT_QUERY_TEMPLATE, (isCountQuery ? "count(*)" : "*"), domainClass.getSimpleName()));
}
/**
@@ -146,6 +159,34 @@ public class QueryString {
return this;
}
public QueryString withHints(String... hints) {
if (!ObjectUtils.isEmpty(hints)) {
StringBuilder builder = new StringBuilder();
for (String hint : hints) {
builder.append(builder.length() > 0 ? ", " : "");
builder.append(String.format("'%1$s'", hint));
}
return new QueryString(String.format(HINTS_QUERY_TEMPLATE, builder.toString(), query));
}
return this;
}
public QueryString withImport(String importExpression) {
return (StringUtils.hasText(importExpression) ? new QueryString(
String.format(IMPORT_QUERY_TEMPLATE, importExpression, query)) : this);
}
public QueryString withLimit(Integer limit) {
return (limit != null ? new QueryString(String.format(LIMIT_QUERY_TEMPLATE, query, limit)) : this);
}
public QueryString withTrace() {
return new QueryString(String.format(TRACE_QUERY_TEMPLATE, query));
}
/*
* (non-Javadoc)
* @see java.lang.Object#toString()

View File

@@ -109,16 +109,18 @@ public class StringBasedGemfireRepositoryQuery extends GemfireRepositoryQuery {
public Object execute(Object[] parameters) {
QueryMethod localQueryMethod = getQueryMethod();
ParametersParameterAccessor parameterAccessor = new ParametersParameterAccessor(
localQueryMethod.getParameters(), parameters);
QueryString query = (isUserDefinedQuery() ? this.query : this.query.forRegion(
localQueryMethod.getEntityInformation().getJavaType(), template.getRegion()));
ParametersParameterAccessor parameterAccessor = new ParametersParameterAccessor(
localQueryMethod.getParameters(), parameters);
for (Integer index : query.getInParameterIndexes()) {
query = query.bindIn(toCollection(parameterAccessor.getBindableValue(index - 1)));
}
query = applyQueryAnnotationExtensions(localQueryMethod, query);
Collection<?> result = toCollection(template.find(query.toString(), parameters));
if (localQueryMethod.isCollectionQuery()) {
@@ -143,6 +145,29 @@ public class StringBasedGemfireRepositoryQuery extends GemfireRepositoryQuery {
}
}
QueryString applyQueryAnnotationExtensions(final QueryMethod queryMethod, final QueryString queryString) {
QueryString resolvedQueryString = queryString;
if (queryMethod instanceof GemfireQueryMethod) {
GemfireQueryMethod gemfireQueryMethod = (GemfireQueryMethod) queryMethod;
String query = queryString.toString().toUpperCase();
if (gemfireQueryMethod.hasImport() && !QueryString.IMPORT_PATTERN.matcher(query).find()) {
resolvedQueryString = resolvedQueryString.withImport(gemfireQueryMethod.getImport());
}
if (gemfireQueryMethod.hasHint() && !QueryString.HINT_PATTERN.matcher(query).find()) {
resolvedQueryString = resolvedQueryString.withHints(gemfireQueryMethod.getHints());
}
if (gemfireQueryMethod.hasLimit() && !QueryString.LIMIT_PATTERN.matcher(query).find()) {
resolvedQueryString = resolvedQueryString.withLimit(gemfireQueryMethod.getLimit());
}
if (gemfireQueryMethod.hasTrace() && !QueryString.TRACE_PATTERN.matcher(query).find()) {
resolvedQueryString = resolvedQueryString.withTrace();
}
}
return resolvedQueryString;
}
boolean isSingleResultNonEntityQuery(QueryMethod method, Collection<?> result) {
return (!method.isCollectionQuery() && method.getReturnedObjectType() != null

View File

@@ -64,7 +64,7 @@ public class GemfireRepositoryFactory extends RepositoryFactorySupport {
public GemfireRepositoryFactory(Iterable<Region<?, ?>> regions, MappingContext<? extends GemfirePersistentEntity<?>,
GemfirePersistentProperty> context) {
Assert.notNull(regions);
this.context = context == null ? new GemfireMappingContext() : context;
this.context = (context != null ? context : new GemfireMappingContext());
this.regions = new Regions(regions, this.context);
}
@@ -108,8 +108,8 @@ public class GemfireRepositoryFactory extends RepositoryFactorySupport {
Region<?, ?> region = regions.getRegion(regionName);
if (region == null) {
throw new IllegalStateException(String.format("No region '%s' found for domain class %s!"
+ " Make sure you have configured a Gemfire region of that name in your application context!",
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()));
}
@@ -118,7 +118,7 @@ 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 %s but the entity to be stored has an id of type %s!",
"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));
}

View File

@@ -16,10 +16,12 @@
package org.springframework.data.gemfire.mapping;
import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.CoreMatchers.notNullValue;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertSame;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue;
import static org.mockito.Matchers.any;
import static org.mockito.Mockito.mock;
@@ -32,6 +34,9 @@ import java.util.List;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.runners.MockitoJUnitRunner;
import org.springframework.data.gemfire.repository.sample.GuestUser;
import org.springframework.data.gemfire.repository.sample.RootUser;
import org.springframework.data.gemfire.repository.sample.User;
@@ -44,13 +49,17 @@ import com.gemstone.gemfire.cache.Region;
*
* @author John J. Blum
* @see org.junit.Test
* @see org.junit.runner.RunWith
* @see org.mockito.Mockito
* @see org.mockito.runners.MockitoJUnitRunner
* @see org.springframework.data.gemfire.mapping.Regions
* @since 1.3.4
*/
@SuppressWarnings("unchecked")
@RunWith(MockitoJUnitRunner.class)
public class RegionsTest {
@Mock
private MappingContext mockMappingContext;
private Region mockUsers;
@@ -59,14 +68,14 @@ public class RegionsTest {
private Regions regions;
protected Region createMockRegion(final String fullPath) {
protected Region mockRegion(final String fullPath) {
// NOTE if the Region path does not contain a "/" then lastIndexOf returns -1 and substring is appropriately
// based on a 0 index then by adding 1, ;-)
return createMockRegion(fullPath.substring(fullPath.lastIndexOf(Region.SEPARATOR) + 1), fullPath);
return mockRegion(fullPath.substring(fullPath.lastIndexOf(Region.SEPARATOR) + 1), fullPath);
}
protected Region createMockRegion(final String name, final String fullPath) {
Region mockRegion = mock(com.gemstone.gemfire.cache.Region.class, name);
protected Region mockRegion(final String name, final String fullPath) {
Region mockRegion = mock(Region.class, name);
when(mockRegion.getName()).thenReturn(name);
when(mockRegion.getFullPath()).thenReturn(fullPath);
@@ -76,27 +85,24 @@ public class RegionsTest {
@Before
public void setup() {
mockMappingContext = mock(GemfireMappingContext.class, "GemfireMappingContext");
mockUsers = createMockRegion("/Users");
mockAdminUsers = createMockRegion("/Users/Admin");
mockGuestUsers = createMockRegion("/Users/Guest");
mockUsers = mockRegion("/Users");
mockAdminUsers = mockRegion("/Users/Admin");
mockGuestUsers = mockRegion("/Users/Guest");
regions = new Regions(Arrays.<Region<?, ?>>asList(mockUsers, mockAdminUsers, mockGuestUsers),
mockMappingContext);
assertNotNull(regions);
assertThat(regions, is(notNullValue()));
}
@After
public void tearDown() {
mockMappingContext = null;
mockUsers = mockAdminUsers = mockGuestUsers = null;
regions = null;
}
@Test
public void testIterateRegions() {
public void iterateRegions() {
List<Region> actualRegions = new ArrayList<Region>(3);
for (Region region : regions) {
@@ -110,7 +116,7 @@ public class RegionsTest {
}
@Test
public void testGetRegionByNameOrPath() {
public void getRegionByNameOrPath() {
assertSame(mockUsers, regions.getRegion("Users"));
assertSame(mockUsers, regions.getRegion("/Users"));
assertSame(mockAdminUsers, regions.getRegion("Admin"));
@@ -120,12 +126,21 @@ public class RegionsTest {
}
@Test(expected = IllegalArgumentException.class)
public void testGetRegionByNameOrPathWithNullArgument() {
public void getRegionByNameOrPathWithNullArgument() {
regions.getRegion((String) null);
}
@Test
public void testGetRegionByDomainClassAndUsingPersistentEntity() {
public void getRegionByDomainClassType() {
when(mockMappingContext.getPersistentEntity(Object.class)).thenReturn(null);
assertSame(mockUsers, regions.getRegion(Users.class));
assertNull(regions.getRegion(User.class));
assertNull(regions.getRegion(RootUser.class));
assertNull(regions.getRegion(GuestUser.class));
}
@Test
public void getRegionByDomainClassTypeAndPersistentEntity() {
GemfirePersistentEntity mockUsersEntity = mock(GemfirePersistentEntity.class, "UsersGemfirePeristentEntity");
GemfirePersistentEntity mockAdminUserEntity = mock(GemfirePersistentEntity.class, "AdminUserGemfirePeristentEntity");
GemfirePersistentEntity mockGuestUserEntity = mock(GemfirePersistentEntity.class, "GuestUserGemfirePeristentEntity");
@@ -145,16 +160,7 @@ public class RegionsTest {
}
@Test
public void testGetRegionByDomainClass() {
when(mockMappingContext.getPersistentEntity(Object.class)).thenReturn(null);
assertSame(mockUsers, regions.getRegion(Users.class));
assertNull(regions.getRegion(User.class));
assertNull(regions.getRegion(RootUser.class));
assertNull(regions.getRegion(GuestUser.class));
}
@Test
public void testGetRegionByPersistentEntity() {
public void getRegionByPersistentEntity() {
GemfirePersistentEntity mockPersistentEntity = mock(GemfirePersistentEntity.class, "GemfirePersistentEntity");
when(mockMappingContext.getPersistentEntity(any(Class.class))).thenReturn(mockPersistentEntity);
@@ -165,7 +171,7 @@ public class RegionsTest {
assertNull(regions.getRegion(GuestUser.class));
}
protected static interface Users {
protected interface Users {
}
}

View File

@@ -13,6 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.gemfire.repository.query;
import static org.hamcrest.CoreMatchers.equalTo;
@@ -48,6 +49,7 @@ import org.springframework.util.ObjectUtils;
* Unit tests for {@link GemfireQueryMethod}.
*
* @author Oliver Gierke
* @author John Blum
*/
@RunWith(MockitoJUnitRunner.class)
public class GemfireQueryMethodUnitTests {

View File

@@ -0,0 +1,110 @@
/*
* 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.query;
import static org.hamcrest.CoreMatchers.equalTo;
import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.CoreMatchers.notNullValue;
import static org.hamcrest.CoreMatchers.nullValue;
import static org.junit.Assert.assertThat;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
import org.springframework.data.gemfire.mapping.GemfirePersistentEntity;
import org.springframework.data.repository.query.parser.PartTree;
/**
* The QueryBuilderTest class is a test suite of test cases testing the contract and functionality of the QueryBuilder
* class.
*
* @author John Blum
* @see org.junit.Rule
* @see org.junit.Test
* @see org.mockito.Mockito
* @see org.springframework.data.gemfire.repository.query.QueryBuilder
* @since 1.7.0
*/
public class QueryBuilderTest {
@Rule
public ExpectedException expectedException = ExpectedException.none();
@Test
public void createQueryBuilderNonDistinct() {
GemfirePersistentEntity<?> mockPersistentEntity = mock(GemfirePersistentEntity.class, "MockGemfirePersistentEntity");
PartTree mockPartTree = mock(PartTree.class, "MockPartTree");
when(mockPersistentEntity.getRegionName()).thenReturn("Example");
when(mockPartTree.isDistinct()).thenReturn(false);
QueryBuilder queryBuilder = new QueryBuilder(mockPersistentEntity, mockPartTree);
assertThat(queryBuilder.toString(), is(equalTo("SELECT * FROM /Example x")));
verify(mockPersistentEntity, times(1)).getRegionName();
verify(mockPartTree, times(1)).isDistinct();
}
@Test
public void createQueryBuilderWithDistinct() {
GemfirePersistentEntity<?> mockPersistentEntity = mock(GemfirePersistentEntity.class, "MockGemfirePersistentEntity");
PartTree mockPartTree = mock(PartTree.class, "MockPartTree");
when(mockPersistentEntity.getRegionName()).thenReturn("Example");
when(mockPartTree.isDistinct()).thenReturn(true);
QueryBuilder queryBuilder = new QueryBuilder(mockPersistentEntity, mockPartTree);
assertThat(queryBuilder.toString(), is(equalTo("SELECT DISTINCT * FROM /Example x")));
verify(mockPersistentEntity, times(1)).getRegionName();
verify(mockPartTree, times(1)).isDistinct();
}
@Test
public void createQueryBuilderWithNullQueryString() {
expectedException.expect(IllegalArgumentException.class);
expectedException.expectCause(is(nullValue(Throwable.class)));
expectedException.expectMessage(is(equalTo("The OQL Query must be specified")));
new QueryBuilder(null);
}
@Test
public void createWithPredicate() {
Predicate mockPredicate = mock(Predicate.class, "MockPredicate");
when(mockPredicate.toString(eq(QueryBuilder.DEFAULT_ALIAS))).thenReturn("x.id = 1");
QueryBuilder queryBuilder = new QueryBuilder(String.format("SELECT * FROM /Example %s",
QueryBuilder.DEFAULT_ALIAS));
QueryString queryString = queryBuilder.create(mockPredicate);
assertThat(queryString, is(notNullValue()));
assertThat(queryString.toString(), is(equalTo("SELECT * FROM /Example x WHERE x.id = 1")));
verify(mockPredicate, times(1)).toString(eq(QueryBuilder.DEFAULT_ALIAS));
}
}

View File

@@ -13,10 +13,13 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.gemfire.repository.query;
import static org.hamcrest.CoreMatchers.equalTo;
import static org.hamcrest.CoreMatchers.is;
import static org.junit.Assert.assertEquals;
import static org.hamcrest.CoreMatchers.notNullValue;
import static org.hamcrest.CoreMatchers.sameInstance;
import static org.junit.Assert.assertThat;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
@@ -59,6 +62,82 @@ public class QueryStringUnitTests {
return new Sort(orders);
}
@Test
public void createQueryStringWithCount() {
QueryString queryString = new QueryString(Person.class, true);
assertThat(queryString, is(notNullValue()));
assertThat(queryString.toString(), is(equalTo("SELECT count(*) FROM /Person")));
}
@Test
public void createQueryStringWithoutCount() {
QueryString queryString = new QueryString(Person.class);
assertThat(queryString, is(notNullValue()));
assertThat(queryString.toString(), is(equalTo("SELECT * FROM /Person")));
}
@Test
public void hintPatternMatches() {
assertThat(QueryString.HINT_PATTERN.matcher("<HINT 'ExampleIdx'>").find(), is(true));
assertThat(QueryString.HINT_PATTERN.matcher("<HINT 'IdIdx'> SELECT * FROM /Example WHERE id = $1").find(), is(true));
assertThat(QueryString.HINT_PATTERN.matcher("<HINT 'LastNameIdx', 'BirthDateIdx'> SELECT * FROM /Person WHERE lastName = $1 AND birthDate = $2").find(), is(true));
}
@Test
public void hintPatternNoMatches() {
assertThat(QueryString.HINT_PATTERN.matcher("HINT").find(), is(false));
assertThat(QueryString.HINT_PATTERN.matcher("<HINT>").find(), is(false));
assertThat(QueryString.HINT_PATTERN.matcher("<HINT ''>").find(), is(false));
assertThat(QueryString.HINT_PATTERN.matcher("<HINT IdIdx>").find(), is(false));
assertThat(QueryString.HINT_PATTERN.matcher("<HINT IdIdx, LastNameIdx>").find(), is(false));
assertThat(QueryString.HINT_PATTERN.matcher("SELECT * FROM /Example").find(), is(false));
assertThat(QueryString.HINT_PATTERN.matcher("SELECT * FROM /Hint").find(), is(false));
assertThat(QueryString.HINT_PATTERN.matcher("SELECT x.hint FROM /Clues x WHERE x.hint > $1").find(), is(false));
}
@Test
public void importPatternMatches() {
assertThat(QueryString.IMPORT_PATTERN.matcher("IMPORT *;").find(), is(true));
assertThat(QueryString.IMPORT_PATTERN.matcher("IMPORT org.example.*;").find(), is(true));
assertThat(QueryString.IMPORT_PATTERN.matcher("IMPORT org.example.Type;").find(), is(true));
assertThat(QueryString.IMPORT_PATTERN.matcher("IMPORT org.example.app.DomainType; SELECT * FROM /Domain").find(), is(true));
}
@Test
public void importPatternNoMatches() {
assertThat(QueryString.IMPORT_PATTERN.matcher("IMPORT").find(), is(false));
assertThat(QueryString.IMPORT_PATTERN.matcher("IMPORT ;").find(), is(false));
assertThat(QueryString.IMPORT_PATTERN.matcher("IMPORT *").find(), is(false));
assertThat(QueryString.IMPORT_PATTERN.matcher("SELECT * FROM /Example").find(), is(false));
}
@Test
public void limitPatternMatches() {
assertThat(QueryString.LIMIT_PATTERN.matcher("LIMIT 10").find(), is(true));
assertThat(QueryString.LIMIT_PATTERN.matcher("SELECT * FROM /Example LIMIT 10").find(), is(true));
assertThat(QueryString.LIMIT_PATTERN.matcher("SELECT * FROM /Example WHERE id = $1 LIMIT 10").find(), is(true));
}
@Test
public void limitPatternNoMatches() {
assertThat(QueryString.LIMIT_PATTERN.matcher("LIMIT").find(), is(false));
assertThat(QueryString.LIMIT_PATTERN.matcher("LIMIT lO").find(), is(false));
assertThat(QueryString.LIMIT_PATTERN.matcher("LIMIT FF").find(), is(false));
assertThat(QueryString.LIMIT_PATTERN.matcher("LIMIT ten").find(), is(false));
assertThat(QueryString.LIMIT_PATTERN.matcher("SELECT * FROM /Example LIMIT").find(), is(false));
assertThat(QueryString.LIMIT_PATTERN.matcher("SELECT * FROM /Example WHERE id = $1 LIM 10").find(), is(false));
}
@Test
public void tracePatternMatches() {
assertThat(QueryString.TRACE_PATTERN.matcher("<TRACE>").find(), is(true));
assertThat(QueryString.TRACE_PATTERN.matcher("<TRACE> SELECT * FROM /Example").find(), is(true));
assertThat(QueryString.TRACE_PATTERN.matcher("<TRACE>SELECT * FROM /Example").find(), is(true));
assertThat(QueryString.TRACE_PATTERN.matcher("SELECT * FROM /Example<TRACE>").find(), is(true));
}
// SGF-251
@Test
public void replacesDomainObjectWithRegionPathCorrectly() {
@@ -79,19 +158,21 @@ public class QueryStringUnitTests {
when(region.getFullPath()).thenReturn("/People");
assertThat(query.forRegion(Person.class, region).toString(), is("SELECT * FROM /People p WHERE p.firstname = $1"));
assertThat(query.forRegion(Person.class, region).toString(),
is("SELECT * FROM /People p WHERE p.firstname = $1"));
verify(region, never()).getName();
}
// SGF-252
@Test
public void replacesFullyQualifiedSubregionReferenceWithRegionPathCorrectly() {
public void replacesFullyQualifiedSubRegionReferenceWithRegionPathCorrectly() {
QueryString query = new QueryString("SELECT * FROM //Local/Root/Users u WHERE u.username = $1");
when(region.getFullPath()).thenReturn("/Local/Root/Users");
assertThat(query.forRegion(RootUser.class, region).toString(), is("SELECT * FROM /Local/Root/Users u WHERE u.username = $1"));
assertThat(query.forRegion(RootUser.class, region).toString(), is(equalTo(
"SELECT * FROM /Local/Root/Users u WHERE u.username = $1")));
verify(region, never()).getName();
}
@@ -110,13 +191,20 @@ public class QueryStringUnitTests {
assertThat(indexes, is((Iterable<Integer>) Arrays.asList(1, 2)));
}
@Test
public void addsNoOrderByClauseCorrectly() {
QueryString query = new QueryString("SELECT * FROM /People p").orderBy(null);
assertThat(query.toString(), is(equalTo("SELECT * FROM /People p")));
}
@Test
public void addsOrderByClauseCorrectly() {
QueryString query = new QueryString("SELECT * FROM /People p WHERE p.lastName = $1")
.orderBy(createSort(createOrder("lastName", Sort.Direction.DESC), createOrder("firstName")));
assertEquals("SELECT * FROM /People p WHERE p.lastName = $1 ORDER BY lastName DESC, firstName ASC",
query.toString());
assertThat(query.toString(), is(equalTo(
"SELECT * FROM /People p WHERE p.lastName = $1 ORDER BY lastName DESC, firstName ASC")));
}
@Test
@@ -124,15 +212,74 @@ public class QueryStringUnitTests {
QueryString query = new QueryString("SELECT DISTINCT p.lastName FROM /People p WHERE p.firstName = $1")
.orderBy(createSort(createOrder("lastName")));
assertEquals("SELECT DISTINCT p.lastName FROM /People p WHERE p.firstName = $1 ORDER BY lastName ASC",
query.toString());
assertThat(query.toString(), is(equalTo(
"SELECT DISTINCT p.lastName FROM /People p WHERE p.firstName = $1 ORDER BY lastName ASC")));
}
@Test
public void addsNoOrderByClauseCorrectly() {
QueryString query = new QueryString("SELECT * FROM /People p").orderBy(null);
public void withHints() {
assertThat(new QueryString("SELECT * FROM /Example").withHints("IdIdx").toString(),
is(equalTo("<HINT 'IdIdx'> SELECT * FROM /Example")));
assertThat(new QueryString("SELECT * FROM /Example").withHints("IdIdx", "SpatialIdx", "TxDateIdx").toString(),
is(equalTo("<HINT 'IdIdx', 'SpatialIdx', 'TxDateIdx'> SELECT * FROM /Example")));
}
assertEquals("SELECT * FROM /People p", query.toString());
@Test
public void withoutHints() {
QueryString expectedQueryString = new QueryString("SELECT * FROM /Example");
assertThat(expectedQueryString.withHints((String[]) null), is(sameInstance(expectedQueryString)));
assertThat(expectedQueryString.withHints(), is(sameInstance(expectedQueryString)));
}
@Test
public void withImport() {
assertThat(new QueryString("SELECT * FROM /People").withImport("org.example.app.domain.Person").toString(),
is(equalTo("IMPORT org.example.app.domain.Person; SELECT * FROM /People")));
}
@Test
public void withoutImport() {
QueryString expectedQueryString = new QueryString("SELECT * FROM /Example");
assertThat(expectedQueryString.withImport(null), is(sameInstance(expectedQueryString)));
assertThat(expectedQueryString.withImport(""), is(sameInstance(expectedQueryString)));
assertThat(expectedQueryString.withImport(" "), is(sameInstance(expectedQueryString)));
}
@Test
public void withLimit() {
assertThat(new QueryString("SELECT * FROM /Example").withLimit(10).toString(),
is(equalTo("SELECT * FROM /Example LIMIT 10")));
assertThat(new QueryString("SELECT * FROM /Example").withLimit(-5).toString(),
is(equalTo("SELECT * FROM /Example LIMIT -5")));
assertThat(new QueryString("SELECT * FROM /Example").withLimit(0).toString(),
is(equalTo("SELECT * FROM /Example LIMIT 0")));
}
@Test
public void withoutLimit() {
QueryString expectedQueryString = new QueryString("SELECT * FROM /Example");
assertThat(expectedQueryString.withLimit(null), is(sameInstance(expectedQueryString)));
}
@Test
public void withTrace() {
assertThat(new QueryString("SELECT * FROM /Example").withTrace().toString(),
is(equalTo("<TRACE> SELECT * FROM /Example")));
}
@Test
public void withHintAndTrace() {
assertThat(new QueryString("SELECT * FROM /Example").withHints("IdIdx").withTrace().toString(),
is(equalTo("<TRACE> <HINT 'IdIdx'> SELECT * FROM /Example")));
}
@Test
public void withHintImportLimitAndTrace() {
assertThat(new QueryString("SELECT * FROM /Example").withImport("org.example.domain.Type")
.withHints("IdIdx", "NameIdx").withLimit(20).withTrace().toString(),
is(equalTo("<TRACE> <HINT 'IdIdx', 'NameIdx'> IMPORT org.example.domain.Type; SELECT * FROM /Example LIMIT 20")));
}
}

View File

@@ -16,17 +16,27 @@
package org.springframework.data.gemfire.repository.query;
import static org.hamcrest.CoreMatchers.equalTo;
import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.CoreMatchers.not;
import static org.hamcrest.CoreMatchers.notNullValue;
import static org.hamcrest.CoreMatchers.sameInstance;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNotSame;
import static org.junit.Assert.assertSame;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue;
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.when;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import org.junit.Test;
@@ -37,7 +47,7 @@ import com.gemstone.gemfire.cache.query.internal.ResultsBag;
/**
* The SpringBasedGemfireRepositoryQueryTest class is a test suite of test cases testing the contract and functionality
* of the StringBasedGemfireRepositoryQuery class.
* <p/>
*
* @author John Blum
* @see org.mockito.Mockito
* @see org.junit.Test
@@ -110,4 +120,103 @@ public class StringBasedGemfireRepositoryQueryTest {
assertTrue(list.isEmpty());
}
@Test
public void applyAllQueryAnnotationExtensions() {
GemfireQueryMethod mockQueryMethod = mock(GemfireQueryMethod.class, "MockGemfireQueryMethod");
when(mockQueryMethod.hasHint()).thenReturn(true);
when(mockQueryMethod.getHints()).thenReturn(Arrays.asList("IdIdx", "NameIdx").toArray(new String[2]));
when(mockQueryMethod.hasImport()).thenReturn(true);
when(mockQueryMethod.getImport()).thenReturn("org.example.domain.Type");
when(mockQueryMethod.hasLimit()).thenReturn(true);
when(mockQueryMethod.getLimit()).thenReturn(10);
when(mockQueryMethod.hasTrace()).thenReturn(true);
QueryString queryString = new QueryString("SELECT * FROM /Example");
assertThat(queryString.toString(), is(equalTo("SELECT * FROM /Example")));
StringBasedGemfireRepositoryQuery repositoryQuery = new StringBasedGemfireRepositoryQuery();
QueryString actualQueryString = repositoryQuery.applyQueryAnnotationExtensions(mockQueryMethod, queryString);
assertThat(actualQueryString, is(notNullValue()));
assertThat(actualQueryString, is(not(sameInstance(queryString))));
assertThat(actualQueryString.toString(), is(equalTo(
"<TRACE> <HINT 'IdIdx', 'NameIdx'> IMPORT org.example.domain.Type; SELECT * FROM /Example LIMIT 10")));
verify(mockQueryMethod, times(1)).hasHint();
verify(mockQueryMethod, times(1)).getHints();
verify(mockQueryMethod, times(1)).hasImport();
verify(mockQueryMethod, times(1)).getImport();
verify(mockQueryMethod, times(1)).hasLimit();
verify(mockQueryMethod, times(1)).getLimit();
verify(mockQueryMethod, times(1)).hasTrace();
}
@Test
public void applyHintLimitAndTraceQueryAnnotationExtensionsWithExistingHintAndLimit() {
GemfireQueryMethod mockQueryMethod = mock(GemfireQueryMethod.class, "MockGemfireQueryMethod");
when(mockQueryMethod.hasHint()).thenReturn(true);
when(mockQueryMethod.getHints()).thenReturn(Collections.singletonList("FirstNameIdx").toArray(new String[1]));
when(mockQueryMethod.hasImport()).thenReturn(false);
when(mockQueryMethod.hasLimit()).thenReturn(true);
when(mockQueryMethod.getLimit()).thenReturn(50);
when(mockQueryMethod.hasTrace()).thenReturn(true);
QueryString queryString = new QueryString("<HINT 'LastNameIdx'> SELECT * FROM /Example LIMIT 25");
assertThat(queryString.toString(), is(equalTo("<HINT 'LastNameIdx'> SELECT * FROM /Example LIMIT 25")));
StringBasedGemfireRepositoryQuery repositoryQuery = new StringBasedGemfireRepositoryQuery();
QueryString actualQueryString = repositoryQuery.applyQueryAnnotationExtensions(mockQueryMethod, queryString);
assertThat(actualQueryString, is(notNullValue()));
assertThat(actualQueryString, is(not(sameInstance(queryString))));
assertThat(actualQueryString.toString(), is(equalTo(
"<TRACE> <HINT 'LastNameIdx'> SELECT * FROM /Example LIMIT 25")));
verify(mockQueryMethod, times(1)).hasHint();
verify(mockQueryMethod, never()).getHints();
verify(mockQueryMethod, times(1)).hasImport();
verify(mockQueryMethod, never()).getImport();
verify(mockQueryMethod, times(1)).hasLimit();
verify(mockQueryMethod, never()).getLimit();
verify(mockQueryMethod, times(1)).hasTrace();
}
@Test
public void applyImportAndTraceQueryAnnotationExtensionsWithExistingTrace() {
GemfireQueryMethod mockQueryMethod = mock(GemfireQueryMethod.class, "MockGemfireQueryMethod");
when(mockQueryMethod.hasHint()).thenReturn(false);
when(mockQueryMethod.hasImport()).thenReturn(true);
when(mockQueryMethod.getImport()).thenReturn("org.example.domain.Type");
when(mockQueryMethod.hasLimit()).thenReturn(false);
when(mockQueryMethod.hasTrace()).thenReturn(true);
QueryString queryString = new QueryString("<TRACE> SELECT * FROM /Example");
assertThat(queryString.toString(), is(equalTo("<TRACE> SELECT * FROM /Example")));
StringBasedGemfireRepositoryQuery repositoryQuery = new StringBasedGemfireRepositoryQuery();
QueryString actualQueryString = repositoryQuery.applyQueryAnnotationExtensions(mockQueryMethod, queryString);
assertThat(actualQueryString, is(notNullValue()));
assertThat(actualQueryString, is(not(sameInstance(queryString))));
assertThat(actualQueryString.toString(), is(equalTo(
"IMPORT org.example.domain.Type; <TRACE> SELECT * FROM /Example")));
verify(mockQueryMethod, times(1)).hasHint();
verify(mockQueryMethod, never()).getHints();
verify(mockQueryMethod, times(1)).hasImport();
verify(mockQueryMethod, times(1)).getImport();
verify(mockQueryMethod, times(1)).hasLimit();
verify(mockQueryMethod, never()).getLimit();
verify(mockQueryMethod, times(1)).hasTrace();
}
}