From dff74e694c6faa686ab63190039b63ae93814cdd Mon Sep 17 00:00:00 2001 From: John Blum Date: Fri, 14 Aug 2015 12:34:42 -0700 Subject: [PATCH] SGF-392 - Add support for OQL Query statement extensions in Repository Query methods via Annotations. Reference Guide updates for OQL Query Langauge extension support via Annotations. --- src/asciidoc/index.adoc | 9 +- src/asciidoc/introduction/new-features.adoc | 16 ++ src/asciidoc/reference/region.adoc | 2 + src/asciidoc/reference/repositories.adoc | 164 ++++++++++++++++++-- 4 files changed, 181 insertions(+), 10 deletions(-) diff --git a/src/asciidoc/index.adoc b/src/asciidoc/index.adoc index 9fd276b7..f0905de7 100644 --- a/src/asciidoc/index.adoc +++ b/src/asciidoc/index.adoc @@ -19,17 +19,19 @@ toc::[] include::{baseDir}/preface.adoc[] :leveloffset: 0 - :leveloffset: +1 + include::{baseDir}/introduction/introduction.adoc[] include::{baseDir}/introduction/requirements.adoc[] include::{baseDir}/introduction/new-features.adoc[] + :leveloffset: -1 [[reference]] = Reference Guide :leveloffset: +1 + include::{baseDir}/reference/introduction.adoc[] include::{baseDir}/reference/bootstrap.adoc[] include::{baseDir}/reference/data.adoc[] @@ -39,6 +41,7 @@ include::{baseDir}/reference/repositories.adoc[] include::{baseDir}/reference/function-annotations.adoc[] include::{baseDir}/reference/gemfire-bootstrap.adoc[] include::{baseDir}/reference/samples.adoc[] + :leveloffset: -1 [[resources]] @@ -47,16 +50,20 @@ include::{baseDir}/reference/samples.adoc[] In addition to this reference documentation, there are a number of other resources that may help you learn how to use GemFire and Spring framework. These additional, third-party resources are enumerated in this section. :leveloffset: +1 + include::{baseDir}/links.adoc[] + :leveloffset: -1 [[appendices]] = Appendices :numbered!: + :leveloffset: +1 include::{spring-data-commons-docs}/repository-namespace-reference.adoc[] include::{spring-data-commons-docs}/repository-populator-namespace-reference.adoc[] include::{spring-data-commons-docs}/repository-query-keywords-reference.adoc[] include::{spring-data-commons-docs}/repository-query-return-types-reference.adoc[] include::{baseDir}/appendix/appendix-schema.adoc[] + :leveloffset: -1 diff --git a/src/asciidoc/introduction/new-features.adoc b/src/asciidoc/introduction/new-features.adoc index 135a32a8..86c8909c 100644 --- a/src/asciidoc/introduction/new-features.adoc +++ b/src/asciidoc/introduction/new-features.adoc @@ -96,3 +96,19 @@ as required by GemFire. * Adds support for GemFire 8's Region data compression. * Adds attributes to set both critical and warning percentages on Disk Store usage. * Supports the capability to add the new EventSubstitutionFilters to GatewaySenders. + +[[new-in-1-7-0]] +== New in the 1.7 Release + +* Upgrades Spring Data GemFire to GemFire 8.1.0 +* Support for adding Spring defined `CacheListener`, `CacheLoader` and `CacheWriter` beans on "existing" GemFire Regions +(perhaps Regions defined in `cache.xml` or using GemFire's Cluster Config) +* Support for Spring JavaConfig in SpringContextBootstrappingInitializer. +* Support for custom ClassLoaders in SpringContextBootstrappingInitializer used to load defined Spring bean classes. +* Support for variable Locator and Server endpoint lists in the `` element using the new `locators` +and `servers` attributes, respectively. +* Allow extensibility of the `MappingPdxSerializer`. +* Enables the use of `` element with non-Spring configured GemFire Servers. +* Multi-Index definition and support. +* <> +* <> diff --git a/src/asciidoc/reference/region.adoc b/src/asciidoc/reference/region.adoc index 1a87f49d..ded11a0b 100644 --- a/src/asciidoc/reference/region.adoc +++ b/src/asciidoc/reference/region.adoc @@ -621,6 +621,7 @@ for maximum efficiency. For a detailed description of eviction policies, see the GemFire documentation (such as http://gemfire.docs.pivotal.io/latest/userguide/index.html#developing/eviction/how_eviction_works.html[this] page). +[[bootstrap:region:expiration]] == Data Expiration GemFire allows you to control how long entries exist in the cache. Expiration is driven by elapsed time, as opposed to @@ -639,6 +640,7 @@ Region is reset whenever the Idle Timeout is reset for one of its entries. Each of these may be applied to the Region itself or entries in the Region. Spring Data GemFire provides ``, ``, `` and `` Region child elements to specify timeout values and expiration actions. +[[bootstrap:region:expiration:annotation]] == Annotation-based Data Expiration As of Spring Data GemFire 1.7, a developer now has the ability to define Expiration policies and settings on individual diff --git a/src/asciidoc/reference/repositories.adoc b/src/asciidoc/reference/repositories.adoc index 971ff487..7d8a2d00 100644 --- a/src/asciidoc/reference/repositories.adoc +++ b/src/asciidoc/reference/repositories.adoc @@ -3,14 +3,16 @@ == Introduction -Spring Data GemFire provides support to use the Spring Data repository abstraction to easily persist entities into GemFire and execute queries. A general introduction into the repository programming model is been provided http://docs.spring.io/spring-data/data-commons/docs/current/reference/html/#repositories[here]. +Spring Data GemFire provides support to use the Spring Data Repository abstraction to easily persist entities +into GemFire and execute queries. A general introduction to the Repository programming model has been provided +http://docs.spring.io/spring-data/data-commons/docs/current/reference/html/#repositories[here]. [[gemfire-repositories.spring-configuration]] -== Spring configuration +== Spring Configuration -To bootstrap Spring Data repositories you use the `` element from the GemFire namespace: +To bootstrap Spring Data Repositories you use the `` element from the GemFire Data namespace: -.Bootstrap GemFire repositories +.Bootstrap GemFire Repositories ==== [source,xml] ---- @@ -28,14 +30,17 @@ To bootstrap Spring Data repositories you use the `` element fro ---- ==== -This configuration snippet will look for interfaces below the configured base package and create repository instances for those interfaces backed by a `SimpleGemFireRepository`. Note that you have to have your domain classes correctly mapped to configured regions as the bottstrap process will fail otherwise. +This configuration snippet will look for interfaces below the configured base package and create Repository instances +for those interfaces backed by a `SimpleGemFireRepository`. Note that you have to have your domain classes correctly +mapped to configured Regions or the bootstrap process will fail otherwise. [[gemfire-repositories.executing-queries]] -== Executing OQL queries +== Executing OQL Queries -The GemFire repositories allow the definition of query methods to easily execute OQL queries against the Region the managed entity is mapped to. +The GemFire Repositories allow the definition of query methods to easily execute OQL Queries against the Region +the managed entity is mapped to. -.Sample repository +.Sample Repository ==== [source,java] ---- @@ -60,7 +65,10 @@ public interface PersonRepository extends CrudRepository { ---- ==== -The first method listed here will cause the following query to be derived: `SELECT x FROM /myRegion x WHERE x.emailAddress = $1`. The second method works the same way except it's returning all entities found whereas the first one expects a single result value. In case the supported keywords are not sufficient to declare your query or the method name gets to verbose you can annotate the query methods with `@Query` as seen for methods 3 and 4. +The first method listed here will cause the following query to be derived: `SELECT x FROM /MyRegion x WHERE x.emailAddress = $1`. +The second method works the same way except it's returning all entities found whereas the first one expects +a single result value. In case the supported keywords are not sufficient to declare your query or the method name +gets to verbose you can annotate the query methods with `@Query` as seen for methods 3 and 4. [cols="1,2,2", options="header"] .Supported keywords for query methods @@ -121,3 +129,141 @@ The first method listed here will cause the following query to be derived: `SELE | `findByActiveIsFalse()` | `x.active = false` |=== + +[[gemfire-repositories.oql-extension]] +== OQL Query Extensions with Annotations + +Many query languages, such as Pivotal GemFire's OQL (Object Query Language), have extensions that are not directly +supported by the Spring Data Commons Repository infrastructure. + +One of Spring Data Commons' Repository infrastructure goals is to function as the lowest common denominator to maintain +support and portability across the widest array of data stores available and in use for application development today. +Technically, this means developers can access multiple different data stores supported by Spring Data Commons within +their applications by reusing their existing application-specific Repository interfaces, a very convenient and powerful +abstraction. + +To support GemFire's OQL Query language extensions and maintain portability across data stores, Spring Data GemFire +adds support for OQL Query extensions by way of Java Annotations. These new Annotations will be ignored by other +Spring Data Repository implementations (e.g. Spring Data Redis) that don't have similar query language extensions. + +For instance, many data stores will most likely not implement GemFire's OQL `IMPORT` keyword. By implementing `IMPORT` +as an Annotation (`@Import`) rather than as part of the query method signature (specifically, the method 'name'), +this will not interfere with the parsing infrastructure when evaluating the query method name to construct +the appropriate data store language appropriate query. + +Currently, the set of OQL Query language extensions that are supported by Spring Data GemFire include: + +[cols="1,2,2,2", options="header"] +.Supported OQL Query extensions for query methods +|=== +| Keyword +| Annotation +| Description +| Arguments + +| http://gemfire.docs.pivotal.io/latest/userguide/developing/query_index/query_index_hints.html#topic_cfb_mxn_jq[HINT] +| `@Hint` +| OQL Query Index Hints +| `String[]` (e.g. @Hint({ "IdIdx", "TxDateIdx" })) + +| http://gemfire.docs.pivotal.io/latest/userguide/developing/query_select/the_import_statement.html#concept_2E9F15B2FE9041238B54736103396BF7[IMPORT] +| `@Import` +| Qualify application-specific types. +| `String` (e.g. @Import("org.example.app.domain.Type")) + +| http://gemfire.docs.pivotal.io/latest/userguide/developing/query_select/the_select_statement.html#concept_85AE7D6B1E2941ED8BD2A8310A81753E__section_25D7055B33EC47B19B1B70264B39212F[LIMIT] +| `@Limit` +| Limit the returned query result set. +| `Integer` (e.g. @Limit(10); default is Integer.MAX_VALUE) + +| http://gemfire.docs.pivotal.io/latest/userguide/developing/query_additional/query_debugging.html#concept_2D557E24AAB24044A3DB36B3124F6748[TRACE] +| `@Trace` +| Enable OQL Query specific debugging. +| NA +|=== + +As an example, suppose you have a `Customers` application domain type and corresponding GemFire Region along with a +`CustomerRepository` and a query method to lookup `Customers` by last name, like so... + +.Sample Repository +==== +[source,java] +---- +package ...; + +import org.springframework.data.annotation.Id; +import org.springframework.data.gemfire.mapping.Region; +... + +@Region("Customers") +public class Customer ... { + + @Id + private Long id; + + ... +} +---- + +[source,java] +---- +package ...; + +import org.springframework.data.gemfire.repository.GemfireRepository; +... + +public interface CustomerRepository extends GemfireRepository { + + @Trace + @Limit(10) + @Hint("LastNameIdx") + @Import("org.example.app.domain.Customer") + List findByLastName(String lastName); + + ... +} +---- +==== + +This will result in the following OQL Query: + +` IMPORT org.example.app.domain.Customer; SELECT * FROM /Customers c WHERE c.lastName = $1 LIMIT 10` + +Spring Data GemFire's Repository extension support is careful not to create conflicting declaratives when +the Query Annotation extensions are used in combination with the `@Query` annotation. + +For instance, suppose you have a raw `@Query` annotated query method defined in your `CustomerRepository` like so... + +.CustomerRepository +==== +[source,java] +---- +public interface CustomerRepository extends GemfireRepository { + + @Trace + @Limit(10) + @Hint("CustomerIdx") + @Import("org.example.app.domain.Customer") + @Query(" SELECT DISTINCT * FROM /Customers c WHERE c.reputation > $1 ORDER BY c.reputation DESC LIMIT 5") + List findDistinctCustomersByReputationGreaterThanOrderByReputationDesc(Integer reputation); +} +---- +==== + +This query method results in the following OQL Query: + +`IMPORT org.example.app.domain.Customer; SELECT DISTINCT * FROM /Customers c WHERE c.reputation > $1 +ORDER BY c.reputation DESC LIMIT 5` + +As you can see, the `@Limit(10)` annotation will +not+ override the `LIMIT` defined explicitly in the raw query. As well, +`@Hint("CustomerIdx")` annotation does +not+ override the `HINT` explicitly defined in the raw query. Finally, the +`@Trace` annotation is redundant and has no additional effect. + +[NOTE] +==== +The "ReputationIdx" Index is probably not the most sensible index given the number of Customers who will possibly have +the same value for their reputation, which will effectively reduce the effectiveness of the index. Please choose +indexes and other optimizations wisely as an improper or poorly choosen index and have the opposite effect on your +performance given the overhead in maintaining the index. The "ReputationIdx" was only used to serve the purpose +of the example. +====