diff --git a/src/main/java/org/springframework/data/gemfire/config/GemfireDataNamespaceHandler.java b/src/main/java/org/springframework/data/gemfire/config/GemfireDataNamespaceHandler.java index b3655fb8..6e175f2c 100644 --- a/src/main/java/org/springframework/data/gemfire/config/GemfireDataNamespaceHandler.java +++ b/src/main/java/org/springframework/data/gemfire/config/GemfireDataNamespaceHandler.java @@ -29,6 +29,7 @@ import org.springframework.data.repository.config.RepositoryConfigurationExtensi * @author David Turanski * @author Oliver Gierke */ +@SuppressWarnings("unused") class GemfireDataNamespaceHandler extends NamespaceHandlerSupport { @Override @@ -40,4 +41,4 @@ class GemfireDataNamespaceHandler extends NamespaceHandlerSupport { registerBeanDefinitionParser("datasource", new GemfireDataSourceParser()); registerBeanDefinitionParser("json-region-autoproxy", new GemfireRegionAutoProxyParser()); } -} \ No newline at end of file +} diff --git a/src/main/java/org/springframework/data/gemfire/repository/query/GemfireQueryMethod.java b/src/main/java/org/springframework/data/gemfire/repository/query/GemfireQueryMethod.java index 0046b454..3bcc6d09 100644 --- a/src/main/java/org/springframework/data/gemfire/repository/query/GemfireQueryMethod.java +++ b/src/main/java/org/springframework/data/gemfire/repository/query/GemfireQueryMethod.java @@ -22,6 +22,10 @@ import org.springframework.data.domain.Pageable; import org.springframework.data.gemfire.mapping.GemfirePersistentEntity; import org.springframework.data.gemfire.mapping.GemfirePersistentProperty; import org.springframework.data.gemfire.repository.Query; +import org.springframework.data.gemfire.repository.query.annotation.Hint; +import org.springframework.data.gemfire.repository.query.annotation.Import; +import org.springframework.data.gemfire.repository.query.annotation.Limit; +import org.springframework.data.gemfire.repository.query.annotation.Trace; import org.springframework.data.mapping.context.MappingContext; import org.springframework.data.repository.core.RepositoryMetadata; import org.springframework.data.repository.query.QueryMethod; @@ -33,8 +37,11 @@ import org.springframework.util.StringUtils; * * @author Oliver Gierke */ +@SuppressWarnings("unused") public class GemfireQueryMethod extends QueryMethod { + protected static final String[] EMPTY_STRING_ARRAY = new String[0]; + private final Method method; private final GemfirePersistentEntity> entity; @@ -51,30 +58,33 @@ public class GemfireQueryMethod extends QueryMethod { super(method, metadata); Assert.notNull(context); - - for (Class> type : method.getParameterTypes()) { - if (Pageable.class.isAssignableFrom(type)) { - throw new IllegalStateException("Pagination is not supported by Gemfire repositories! Offending method: " - + method.toString()); - } - } + assertNonPagingQueryMethod(method); this.method = method; this.entity = context.getPersistentEntity(getDomainClass()); } /** - * Returns whether the query method contains an annotated, non-empty query. - * - * @return whether the query method contains an annotated, non-empty query. + * Asserts that the query method is a non-Paging query method since GemFire does not support pagination + * as it has no concept of a Cursor. + * + * @param method the query method to be evaluated + * @throws java.lang.IllegalStateException if the query method contains a parameter of type Pageable. + * @see org.springframework.data.domain.Pageable + * @see java.lang.reflect.Method#getParameterTypes() */ - public boolean hasAnnotatedQuery() { - return StringUtils.hasText(getAnnotatedQuery()); + 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()); + } + } } /** * Returns the {@link GemfirePersistentEntity} the method deals with. - * + * * @return the {@link GemfirePersistentEntity} the method deals with. */ public GemfirePersistentEntity> getPersistentEntity() { @@ -82,15 +92,110 @@ public class GemfireQueryMethod extends QueryMethod { } /** - * Returns the query annotated to the query method. + * Determines whether this query method specifies an annotated, non-empty query. + * + * @return a boolean value indicating whether the query method specifies an annotated, non-empty query. + * @see org.springframework.util.StringUtils#hasText(String) + * @see #getAnnotatedQuery() + */ + public boolean hasAnnotatedQuery() { + return StringUtils.hasText(getAnnotatedQuery()); + } + + /** + * Returns the annotated query for the query method if present. * - * @return the annotated query or {@literal null} in case it's empty or none available. + * @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) */ String getAnnotatedQuery() { - Query query = method.getAnnotation(Query.class); - String queryString = query == null ? null : (String) AnnotationUtils.getValue(query); - - return StringUtils.hasText(queryString) ? queryString : null; + String queryString = (query != null ? (String) AnnotationUtils.getValue(query) : null); + return (StringUtils.hasText(queryString) ? queryString : null); } + + /** + * Determines whether this query method uses a query HINT to tell the GemFire OQL query engine which indexes + * to apply to the query execution. + * + * @return a boolean value to indicate whether this query method uses a query HINT. + * @see org.springframework.data.gemfire.repository.query.annotation.Hint + * @see java.lang.reflect.Method#isAnnotationPresent(Class) + */ + public boolean hasHint() { + return method.isAnnotationPresent(Hint.class); + } + + /** + * Gets the query HINTs for this query method. + * + * @return the query HINTs for this query method or an empty array if this query method has no query HINTs. + * @see org.springframework.data.gemfire.repository.query.annotation.Hint + * @see java.lang.reflect.Method#getAnnotation(Class) + */ + public String[] getHints() { + Hint hint = method.getAnnotation(Hint.class); + return (hint != null ? hint.value() : EMPTY_STRING_ARRAY); + } + + /** + * Determine whether this query method declares an IMPORT statement to qualify application domain object types + * referenced in the query. + * + * @return a boolean value to indicate whether this query method declares an IMPORT statement. + * @see org.springframework.data.gemfire.repository.query.annotation.Import + * @see java.lang.reflect.Method#isAnnotationPresent(Class) + */ + public boolean hasImport() { + return method.isAnnotationPresent(Import.class); + } + + /** + * Gets the IMPORT statement for this query method. + * + * @return the IMPORT statement for this query method or null if this query method does not have an IMPORT statement. + * @see org.springframework.data.gemfire.repository.query.annotation.Import + * @see java.lang.reflect.Method#getAnnotation(Class) + */ + public String getImport() { + Import importStatement = method.getAnnotation(Import.class); + return (importStatement != null ? importStatement.value() : null); + } + + /** + * Determines whether this query method defines a LIMIT on the number of results returned by the query. + * + * @return a boolean value indicating whether this query method defines a LIMIT on the result set + * returned by the query. + * @see org.springframework.data.gemfire.repository.query.annotation.Limit + * @see java.lang.reflect.Method#isAnnotationPresent(Class) + */ + public boolean hasLimit() { + return method.isAnnotationPresent(Limit.class); + } + + /** + * Gets the LIMIT for this query method on the result set returned by the query. + * + * @return the LIMIT for this query method limiting the number of results returned by the query. + * @see org.springframework.data.gemfire.repository.query.annotation.Limit + * @see java.lang.reflect.Method#getAnnotation(Class) + */ + public int getLimit() { + Limit limit = method.getAnnotation(Limit.class); + return (limit != null ? limit.value() : Integer.MAX_VALUE); + } + + /** + * Determines whether this query method has TRACE (i.e. logging) enabled. + * + * @return a boolean value to indicate whether this query method has TRACE enabled. + * @see org.springframework.data.gemfire.repository.query.annotation.Limit + * @see java.lang.reflect.Method#isAnnotationPresent(Class) + */ + public boolean hasTrace() { + return method.isAnnotationPresent(Trace.class); + } + } diff --git a/src/main/java/org/springframework/data/gemfire/repository/query/StringBasedGemfireRepositoryQuery.java b/src/main/java/org/springframework/data/gemfire/repository/query/StringBasedGemfireRepositoryQuery.java index ceca605d..f3eff4c0 100644 --- a/src/main/java/org/springframework/data/gemfire/repository/query/StringBasedGemfireRepositoryQuery.java +++ b/src/main/java/org/springframework/data/gemfire/repository/query/StringBasedGemfireRepositoryQuery.java @@ -15,19 +15,19 @@ */ package org.springframework.data.gemfire.repository.query; -import java.util.Arrays; import java.util.Collection; import java.util.Collections; -import com.gemstone.gemfire.cache.query.SelectResults; - import org.springframework.dao.IncorrectResultSizeDataAccessException; import org.springframework.data.gemfire.GemfireTemplate; import org.springframework.data.repository.query.ParametersParameterAccessor; +import org.springframework.data.repository.query.QueryMethod; import org.springframework.util.Assert; import org.springframework.util.CollectionUtils; import org.springframework.util.StringUtils; +import com.gemstone.gemfire.cache.query.SelectResults; + /** * {@link GemfireRepositoryQuery} using plain {@link String} based OQL queries. *
@@ -41,7 +41,6 @@ public class StringBasedGemfireRepositoryQuery extends GemfireRepositoryQuery {
private boolean userDefinedQuery = false;
- private final GemfireQueryMethod method;
private final GemfireTemplate template;
private final QueryString query;
@@ -51,7 +50,6 @@ public class StringBasedGemfireRepositoryQuery extends GemfireRepositoryQuery {
*/
StringBasedGemfireRepositoryQuery() {
query = null;
- method = null;
template = null;
}
@@ -59,11 +57,11 @@ 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 method must not be {@literal null}.
+ * @param queryMethod must not be {@literal null}.
* @param template must not be {@literal null}.
*/
- public StringBasedGemfireRepositoryQuery(GemfireQueryMethod method, GemfireTemplate template) {
- this(method.getAnnotatedQuery(), method, template);
+ public StringBasedGemfireRepositoryQuery(GemfireQueryMethod queryMethod, GemfireTemplate template) {
+ this(queryMethod.getAnnotatedQuery(), queryMethod, template);
}
/**
@@ -71,20 +69,19 @@ public class StringBasedGemfireRepositoryQuery extends GemfireRepositoryQuery {
* {@link GemfireQueryMethod} and {@link GemfireTemplate}.
*
* @param query will fall back to the query annotated to the given {@link GemfireQueryMethod} if {@literal null}.
- * @param method must not be {@literal null}.
+ * @param queryMethod must not be {@literal null}.
* @param template must not be {@literal null}.
*/
- public StringBasedGemfireRepositoryQuery(String query, GemfireQueryMethod method, GemfireTemplate template) {
- super(method);
+ public StringBasedGemfireRepositoryQuery(String query, GemfireQueryMethod queryMethod, GemfireTemplate template) {
+ super(queryMethod);
Assert.notNull(template);
this.userDefinedQuery |= !StringUtils.hasText(query);
- this.query = new QueryString(StringUtils.hasText(query) ? query : method.getAnnotatedQuery());
- this.method = method;
+ this.query = new QueryString(StringUtils.hasText(query) ? query : queryMethod.getAnnotatedQuery());
this.template = template;
- if (method.isPageQuery() || method.isModifyingQuery()) {
+ if (queryMethod.isModifyingQuery() || queryMethod.isPageQuery()) {
throw new IllegalStateException(INVALID_QUERY);
}
}
@@ -110,10 +107,13 @@ public class StringBasedGemfireRepositoryQuery extends GemfireRepositoryQuery {
*/
@Override
public Object execute(Object[] parameters) {
- ParametersParameterAccessor parameterAccessor = new ParametersParameterAccessor(method.getParameters(), parameters);
+ QueryMethod localQueryMethod = getQueryMethod();
+
+ ParametersParameterAccessor parameterAccessor = new ParametersParameterAccessor(
+ localQueryMethod.getParameters(), parameters);
QueryString query = (isUserDefinedQuery() ? this.query : this.query.forRegion(
- method.getEntityInformation().getJavaType(), template.getRegion()));
+ localQueryMethod.getEntityInformation().getJavaType(), template.getRegion()));
for (Integer index : query.getInParameterIndexes()) {
query = query.bindIn(toCollection(parameterAccessor.getBindableValue(index - 1)));
@@ -121,35 +121,35 @@ public class StringBasedGemfireRepositoryQuery extends GemfireRepositoryQuery {
Collection> result = toCollection(template.find(query.toString(), parameters));
- if (method.isCollectionQuery()) {
- return result;
- }
- else if (method.isQueryForEntity()) {
- if (result.isEmpty()) {
- return null;
- }
- else if (result.size() == 1) {
- return result.iterator().next();
- }
- else {
- throw new IncorrectResultSizeDataAccessException(1, result.size());
- }
- }
- else if (isSingleResultNonEntityQuery(method, result)) {
- return result.iterator().next();
- }
- else {
- throw new IllegalStateException("Unsupported query: " + query.toString());
- }
- }
+ if (localQueryMethod.isCollectionQuery()) {
+ return result;
+ }
+ else if (localQueryMethod.isQueryForEntity()) {
+ if (result.isEmpty()) {
+ return null;
+ }
+ else if (result.size() == 1) {
+ return result.iterator().next();
+ }
+ else {
+ throw new IncorrectResultSizeDataAccessException(1, result.size());
+ }
+ }
+ else if (isSingleResultNonEntityQuery(localQueryMethod, result)) {
+ return result.iterator().next();
+ }
+ else {
+ throw new IllegalStateException("Unsupported query: " + query.toString());
+ }
+ }
- boolean isSingleResultNonEntityQuery(final GemfireQueryMethod method, final Collection> result) {
- return (!method.isCollectionQuery() && method.getReturnedObjectType() != null
- && !Void.TYPE.equals(method.getReturnedObjectType()) && result != null && result.size() == 1);
- }
+ boolean isSingleResultNonEntityQuery(QueryMethod method, Collection> result) {
+ return (!method.isCollectionQuery() && method.getReturnedObjectType() != null
+ && !Void.TYPE.equals(method.getReturnedObjectType()) && result != null && result.size() == 1);
+ }
- /**
+ /**
* Returns the given object as a Collection. Collections will be returned as is, Arrays will be converted into a
* Collection and all other objects will be wrapped into a single-element Collection.
*
@@ -158,7 +158,7 @@ public class StringBasedGemfireRepositoryQuery extends GemfireRepositoryQuery {
* @see java.util.Arrays#asList(Object[])
* @see java.util.Collection
* @see org.springframework.util.CollectionUtils#arrayToList(Object)
- * @see com.gemstone.gemfire.cache.query.SelectResults
+ * @see com.gemstone.gemfire.cache.query.SelectResults
*/
Collection> toCollection(final Object source) {
if (source instanceof SelectResults) {
@@ -173,7 +173,7 @@ public class StringBasedGemfireRepositoryQuery extends GemfireRepositoryQuery {
return Collections.emptyList();
}
- return (source.getClass().isArray() ? CollectionUtils.arrayToList(source) : Arrays.asList(source));
+ return (source.getClass().isArray() ? CollectionUtils.arrayToList(source) : Collections.singletonList(source));
}
}
diff --git a/src/main/java/org/springframework/data/gemfire/repository/query/annotation/Hint.java b/src/main/java/org/springframework/data/gemfire/repository/query/annotation/Hint.java
new file mode 100644
index 00000000..6ff1faff
--- /dev/null
+++ b/src/main/java/org/springframework/data/gemfire/repository/query/annotation/Hint.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright 2010-2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.springframework.data.gemfire.repository.query.annotation;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Inherited;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * The Hint class is a annotation type indicating a GemFire OQL Query Hint.
+ *
+ * @author John Blum
+ * @see java.lang.annotation.Documented
+ * @see java.lang.annotation.Inherited
+ * @see java.lang.annotation.Retention
+ * @see java.lang.annotation.Target
+ * @since 1.7.0
+ */
+@Documented
+@Inherited
+@Retention(RetentionPolicy.RUNTIME)
+@Target({ ElementType.METHOD })
+@SuppressWarnings("unused")
+public @interface Hint {
+
+ String[] value() default {};
+
+}
diff --git a/src/main/java/org/springframework/data/gemfire/repository/query/annotation/Import.java b/src/main/java/org/springframework/data/gemfire/repository/query/annotation/Import.java
new file mode 100644
index 00000000..53c7069d
--- /dev/null
+++ b/src/main/java/org/springframework/data/gemfire/repository/query/annotation/Import.java
@@ -0,0 +1,47 @@
+/*
+ * 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.annotation;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Inherited;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * The Import class is a annotation type indicating a GemFire OQL Query Import to refer to the class of an object
+ * in cases where the same class name resides in two different name scopes (packages), then you must be able to
+ * differentiate the classes having the same name.
+ *
+ * @author John Blum
+ * @see java.lang.annotation.Documented
+ * @see java.lang.annotation.Inherited
+ * @see java.lang.annotation.Retention
+ * @see java.lang.annotation.Target
+ * @since 1.7.0
+ */
+@Documented
+@Inherited
+@Retention(RetentionPolicy.RUNTIME)
+@Target({ ElementType.METHOD })
+@SuppressWarnings("unused")
+public @interface Import {
+
+ String value();
+
+}
diff --git a/src/main/java/org/springframework/data/gemfire/repository/query/annotation/Limit.java b/src/main/java/org/springframework/data/gemfire/repository/query/annotation/Limit.java
new file mode 100644
index 00000000..abcd4ac3
--- /dev/null
+++ b/src/main/java/org/springframework/data/gemfire/repository/query/annotation/Limit.java
@@ -0,0 +1,46 @@
+/*
+ * 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.annotation;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Inherited;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * The Limit class is an annotation type indicating a GemFire OQL Query Limit on the number of results
+ * that are returned.
+ *
+ * @author John Blum
+ * @see java.lang.annotation.Documented
+ * @see java.lang.annotation.Inherited
+ * @see java.lang.annotation.Retention
+ * @see java.lang.annotation.Target
+ * @since 1.7.0
+ */
+@Documented
+@Inherited
+@Retention(RetentionPolicy.RUNTIME)
+@Target({ ElementType.METHOD })
+@SuppressWarnings("unused")
+public @interface Limit {
+
+ int value() default Integer.MAX_VALUE;
+
+}
diff --git a/src/main/java/org/springframework/data/gemfire/repository/query/annotation/Trace.java b/src/main/java/org/springframework/data/gemfire/repository/query/annotation/Trace.java
new file mode 100644
index 00000000..c304791f
--- /dev/null
+++ b/src/main/java/org/springframework/data/gemfire/repository/query/annotation/Trace.java
@@ -0,0 +1,43 @@
+/*
+ * 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.annotation;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Inherited;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * The Trace class is an annotation type to enable GemFire OQL Query debugging.
+ *
+ * @author John Blum
+ * @see java.lang.annotation.Documented
+ * @see java.lang.annotation.Inherited
+ * @see java.lang.annotation.Retention
+ * @see java.lang.annotation.Target
+ * @since 1.7.0
+ */
+@Documented
+@Inherited
+@Retention(RetentionPolicy.RUNTIME)
+@Target({ ElementType.METHOD })
+@SuppressWarnings("unused")
+public @interface Trace {
+
+}
diff --git a/src/main/java/org/springframework/data/gemfire/repository/support/GemfireRepositoryFactoryBean.java b/src/main/java/org/springframework/data/gemfire/repository/support/GemfireRepositoryFactoryBean.java
index 3fba3505..ec06c4b3 100644
--- a/src/main/java/org/springframework/data/gemfire/repository/support/GemfireRepositoryFactoryBean.java
+++ b/src/main/java/org/springframework/data/gemfire/repository/support/GemfireRepositoryFactoryBean.java
@@ -38,13 +38,14 @@ import com.gemstone.gemfire.cache.Region;
*
* @author Oliver Gierke
*/
-public class GemfireRepositoryFactoryBean