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:
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
|
||||
|
||||
@@ -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 {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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")));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user