[[gemfire-repositories]]
= Spring Data for Pivotal GemFire Repositories

== Introduction

_Spring Data for Pivotal GemFire_ provides support to use the _Spring Data Repository_ abstraction to easily persist entities
into Pivotal GemFire along with execute queries.  A general introduction to the _Repository programming model_ is provided
http://docs.spring.io/spring-data/data-commons/docs/current/reference/html/#repositories[here].

[[gemfire-repositories.spring-configuration-xml]]
== Spring XML Configuration

To bootstrap _Spring Data Repositories_, you use the `<repositories/>` element from the _Spring Data for Pivotal GemFire_
Data namespace:

.Bootstrap Spring Data for Pivotal GemFire Repositories in XML
====
[source,xml]
----
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:gfe-data="http://www.springframework.org/schema/data/gemfire"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="
         http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
         http://www.springframework.org/schema/data/gemfire http://www.springframework.org/schema/data/gemfire/spring-data-gemfire.xsd>

  <gfe-data:repositories base-package="com.example.acme.repository"/>

</beans>
----
====

This configuration snippet looks for interfaces below the configured base package and creates _Repository_ instances
for those interfaces backed by a `SimplePivotal GemFireRepository`.

IMPORTANT: You must have your application domain classes correctly mapped to configured Regions
or the bootstrap process will fail otherwise.

[[gemfire-repositories.spring-configuration-java]]
== Spring Java-based Configuration

Alternatively, many users prefer to use _Spring's_
https://docs.spring.io/spring/docs/current/spring-framework-reference/core.html#beans-java[Java-based container configuration].

Using this approach, it is a simple matter to bootstrap _Spring Data Repositories_ using the SDG `@EnableGemfireRepositories`
annotation:

.Bootstrap _Spring Data for Pivotal GemFire Repositories_ with `@EnableGemfireRepositories`
====
[source, java]
----
@SpringBootApplication
@EnableGemfireRepositories(basePackages = "com.example.acme.repository")
class SpringDataApplication {
  ...
}
----
====

Rather than use the `basePackages` attribute, you may prefer to use the type-safe `basePackageClasses` attribute instead.
The `basePackageClasses` allows you to specify the package containing all your application _Repository_ classes
by specifying just one of your application _Repository_ interface types.  Consider creating a special no-op marker class
or interface in each package that serves no other purpose than to identify the location of application _Repositories_
referenced by this attribute.

In addition to the `basePackage[sClasses]` attributes, like _Spring's_
https://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/context/annotation/ComponentScan.html[`@ComponentScan`] annotation,
the `@EnableGemfireRepositories` annotation provides _include_ and _exclude_ filters, based on _Spring's_
https://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/context/annotation/ComponentScan.Filter.html[`ComponentScan.Filter`] type.
You can use the `filterType` attribute to filter by different aspects, such as whether an application _Repository_ type
is annotated with a particular `Annotation` or extends a particular class type, and so on.  See the
https://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/context/annotation/FilterType.html[`FilterType` _Javadoc_]
for more details.

The `@EnableGemfireRepositories` annotation also provides the ability to specify the location of named OQL queries,
which reside in a Java `Properties` file, using the `namedQueriesLocation` attribute.  The property name must match
the name of a _Repository_ query method and the property value is the OQL query you want executed when
the _Repository_ query method is called.

The `repositoryImplementationPostfix` attribute can be set to an alternate value (defaults to "_Impl_") if your
application requires 1 or more https://docs.spring.io/spring-data/commons/docs/current/reference/html/#repositories.custom-implementations[custom _Repository_ implementations].
This feature is commonly used to extend the _Spring Data Repository_ infrastructure in order to implement a feature
not provided out-of-the-box (OOTB) by the data store (e.g. SDG).

One example of where custom _Repository_ implementations are needed with Pivotal GemFire is when performing _Joins_.
_Joins_ are not supported by SDG _Repositories_ OOTB.  With a Pivotal GemFire `PARTITION` Region, the _Join_ must be
performed on collocated `PARTITION` Regions even, since Pivotal GemFire does not support "distributed" _Joins_.
In addition, the _Equi-Join_ OQL Query must be performed inside a Pivotal GemFire Function.
See http://gemfire91.docs.pivotal.io/geode/developing/partitioned_regions/join_query_partitioned_regions.html[here]
for more details on Pivotal GemFire _Equi-Join Queries_.

Many other aspects of the SDG's _Repository_ infrastructure extension maybe customized as well.  See the
https://docs.spring.io/spring-data/gemfire/docs/current/api/org/springframework/data/gemfire/repository/config/EnableGemfireRepositories.html[`@EnableGemfireRepositories` _Javadoc_]
for more details on all configuration settings.

[[gemfire-repositories.queries.executing]]
== Executing OQL Queries

_Spring Data for Pivotal GemFire Repositories_ enable the definition of query methods to easily execute Pivotal GemFire OQL Queries
against the Region the managed entity is mapped to.

.Sample Repository
====
[source,java]
----
@Region("People")
public class Person { … }
----

[source,java]
----
public interface PersonRepository extends CrudRepository<Person, Long> {

  Person findByEmailAddress(String emailAddress);

  Collection<Person> findByFirstname(String firstname);

  @Query("SELECT * FROM /People p WHERE p.firstname = $1")
  Collection<Person> findByFirstnameAnnotated(String firstname);

  @Query("SELECT * FROM /People p WHERE p.firstname IN SET $1")
  Collection<Person> findByFirstnamesAnnotated(Collection<String> firstnames);
}
----
====

The first query method listed here will cause the following OQL query to be derived:
`SELECT x FROM /People x WHERE x.emailAddress = $1`.  The second query method works the same way except
it's returning all entities found whereas the first query method expects a single result to be found.

In case the supported keywords are not sufficient to expresss and declare your OQL query, or the method name
becomes too 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
|===
| Keyword
| Sample
| Logical result

| `GreaterThan`
| `findByAgeGreaterThan(int age)`
| `x.age > $1`

| `GreaterThanEqual`
| `findByAgeGreaterThanEqual(int age)`
| `x.age >= $1`

| `LessThan`
| `findByAgeLessThan(int age)`
| `x.age < $1`

| `LessThanEqual`
| `findByAgeLessThanEqual(int age)`
| `x.age <= $1`

| `IsNotNull`, `NotNull`
| `findByFirstnameNotNull()`
| `x.firstname =! NULL`

| `IsNull`, `Null`
| `findByFirstnameNull()`
| `x.firstname = NULL`

| `In`
| `findByFirstnameIn(Collection<String> x)`
| `x.firstname IN SET $1`

| `NotIn`
| `findByFirstnameNotIn(Collection<String> x)`
| `x.firstname NOT IN SET $1`

| `IgnoreCase`
| `findByFirstnameIgnoreCase(String firstName)`
| `x.firstname.equalsIgnoreCase($1)`

| (No keyword)
| `findByFirstname(String name)`
| `x.firstname = $1`

| `Like`
| `findByFirstnameLike(String name)`
| `x.firstname LIKE $1`

| `Not`
| `findByFirstnameNot(String name)`
| `x.firstname != $1`

| `IsTrue`, `True`
| `findByActiveIsTrue()`
| `x.active = true`

| `IsFalse`, `False`
| `findByActiveIsFalse()`
| `x.active = false`
|===

[[gemfire-repositories.queries.oql-extensions]]
== OQL Query Extensions using Annotations

Many query languages, such as Pivotal GemFire's OQL (Object Query Language), have extensions that are not directly
supported by _Spring Data Commons' Repository_ infrastructure.

One of _Spring Data Commons' Repository_ infrastructure goals is to function as the lowest common denominator
in order to maintain support for 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 Pivotal GemFire's OQL Query language extensions and preserve portability across different data stores,
_Spring Data for Pivotal GemFire_ adds support for OQL Query extensions using Java Annotations.  These Annotations will be ignored
by other _Spring Data Repository_ implementations (e.g. _Spring Data_ JPA or _Spring Data Redis_) that do not have
similar query language extensions.

For instance, many data stores will most likely not implement Pivotal GemFire's OQL `IMPORT` keyword.  By implementing `IMPORT`
as an Annotation (i.e. `@Import`) rather than as part of the query method signature (specifically, the method 'name'),
then this will not interfere with the parsing infrastructure when evaluating the query method name to construct
another data store language appropriate query.

Currently, the set of Pivotal GemFire OQL Query language extensions that are supported by _Spring Data for Pivotal GemFire_ include:

[cols="1,2,2,2", options="header"]
.Supported Pivotal GemFire OQL extensions for Repository query methods
|===
| Keyword
| Annotation
| Description
| Arguments

| http://gemfire.docs.pivotal.io/docs-gemfire/latest/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/docs-gemfire/latest/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/docs-gemfire/latest/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/docs-gemfire/latest/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 class and corresponding Pivotal GemFire Region along with a
`CustomerRepository` and a query method to lookup `Customers` by last name, like so...

.Sample Customers Repository
====
[source,java]
----
package ...;

import org.springframework.data.annotation.Id;
import org.springframework.data.gemfire.mapping.annotation.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<Customer, Long> {

  @Trace
  @Limit(10)
  @Hint("LastNameIdx")
  @Import("org.example.app.domain.Customer")
  List<Customer> findByLastName(String lastName);

  ...
}
----
====

This will result in the following OQL Query:

`<TRACE> <HINT 'LastNameIdx'> IMPORT org.example.app.domain.Customer; SELECT * FROM /Customers x WHERE x.lastName = $1 LIMIT 10`

_Spring Data for Pivotal GemFire's Repository_ extension and support is careful not to create conflicting declarations when
the OQL Annotation extensions are used in combination with the `@Query` annotation.

As another example, suppose you have a raw `@Query` annotated query method defined in your `CustomerRepository`
like so...

.CustomerRepository
====
[source,java]
----
public interface CustomerRepository extends GemfireRepository<Customer, Long> {

  @Trace
  @Limit(10)
  @Hint("CustomerIdx")
  @Import("org.example.app.domain.Customer")
  @Query("<TRACE> <HINT 'ReputationIdx'> SELECT DISTINCT * FROM /Customers c WHERE c.reputation > $1 ORDER BY c.reputation DESC LIMIT 5")
  List<Customer> findDistinctCustomersByReputationGreaterThanOrderByReputationDesc(Integer reputation);
}
----
====

This query method results in the following OQL Query:

`IMPORT org.example.app.domain.Customer; <TRACE> <HINT 'ReputationIdx'> SELECT DISTINCT * FROM /Customers x
WHERE x.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 can 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.
====

[[gemfire-repositories.queries.post-processing]]
== Query Post Processing

Using the Spring Data _Repository_ abstraction, query method convention for defining data store specific queries
(e.g. OQL) is easy and convenient.  However, it is sometimes desirable to still want to inspect or even possibly
modify the query "generated" from the _Repository_ query method.

Since 2.0.x, _Spring Data for Pivotal GemFire_ introduces the `o.s.d.gemfire.repository.query.QueryPostProcessor`
functional interface.  The interface is loosely defined as follows...

.QueryPostProcessor
====
[source,java]
----
package org.springframework.data.gemfire.repository.query;

import org.springframework.core.Ordered;
import org.springframework.data.repository.Repository;
import org.springframework.data.repository.query.QueryMethod;
import ...;

@FunctionalInterface
interface QueryPostProcessor<T extends Repository, QUERY> extends Ordered {

  QUERY postProcess(QueryMethod queryMethod, QUERY query, Object... arguments);

}
----
====

There are additional default methods provided to allow users to compose instances of `QueryPostProcessor` very similar
to how https://docs.oracle.com/javase/8/docs/api/java/util/function/Function.html#compose-java.util.function.Function-[java.util.function.Function.andThen(:Function)]
and https://docs.oracle.com/javase/8/docs/api/java/util/function/Function.html#compose-java.util.function.Function-[java.util.function.Function.compose(:Function)]
work.

Additionally, you will notice that the `QueryPostProcessor` interface implements the
https://docs.spring.io/spring/docs/5.0.2.RELEASE/javadoc-api/org/springframework/core/Ordered.html[`org.springframework.core.Ordered`]
interface, which is useful when multiple `QueryPostProcessors` are declared and registered in the Spring context
and used to create a pipeline of processing for a group of generated query method queries.

Finally, the `QueryPostProcessor` accepts type arguments corresponding to the type parameters, `T` and `QUERY`,
respectively.  Type of `T` extends the _Spring Data Commons_ marker interface,
https://docs.spring.io/spring-data/commons/docs/current/api/org/springframework/data/repository/Repository.html[`org.springframework.data.repository.Repository`].
We will discuss this further below.  All `QUERY` type parameter arguments in _Spring Data for Pivotal GemFire's_ case
will be of type `java.lang.String`.

NOTE: It is useful to define the query as type `QUERY` since this `QueryPostProcessor` interface maybe ported to
_Spring Data Commons_ and therefore must handle all forms of queries by different data stores (e.g. JPA, MongoDB,
or Redis).

As user may implement this interface to receive a callback with the query that was generated from the application
`Repository` interface method when the method is called.

For example, I might want to log all queries from all application _Repository_ interface definitions.  I could do so
using the following `QueryPostProcessor` implementation...

.LoggingQueryPostProcessor
====
[source,java]
----
package example;

import ...;

class LoggingQueryPostProcessor implements QueryPostProcessor<Repository, String> {

  private Logger logger = Logger.getLogger("someLoggerName");

  @Override
  public String postProcess(QueryMethod queryMethod, String query, Object... arguments) {

      String message = String.format("Executing query [%s] with arguments [%s]", query, Arrays.toString(arguments));

      this.logger.info(message);
  }
}
----
====

The `LoggingQueryPostProcessor` was typed to the Spring Data `org.springframework.data.repository.Repository`
marker interface, and therefore, will log all application _Repository_ interface query method "generated" queries.

You could limit the scope of this logging to queries only from certain types of application _Repository_ interfaces,
such as, say, an `CustomerRepository`...

.CustomerRepository
====
[source,java]
----
interface CustomerRepository extends CrudRepository<Customer, Long> {

  Customer findByAccountNumber(String accountNumber);

  List<Customer> findByLastNameLike(String lastName);

}
----
====

Then, I could have typed the `LoggingQueryPostProcessor` specifically to the `CustomerRepository`, like so...

.CustomerLoggingQueryPostProcessor
====
[source,java]
----
class LoggingQueryPostProcessor implements QueryPostProcessor<CustomerRepository, String> { .. }
----
====

As result, only queries defined in the `CustomerRepository` interface (e.g. `findByAccountNumber`) would be logged.

I might want to create a `QueryPostProcessor` for a specific query defined by a _Repository_ query method.  For example,
say I want to "`LIMIT`" the OQL query generated from the `CustomerRepository.findByLastNameLike(:String)` query method
to only return 5 results and I want to order the `Customers` by `firstName`, ascending.  Well, then, I can define
a custom `QueryPostProcessor` like so...

.OrderedLimitedCustomerByLastNameQueryPostProcessor
====
[source,java]
----
class OrderedLimitedCustomerByLastNameQueryPostProcessor implements QueryPostProcessor<CustomerRepository, String> {

  private final int limit;

  public OrderedLimitedCustomerByLastNameQueryPostProcessor(int limit) {
    this.limit = limit;
  }

  @Override
  public String postProcess(QueryMethod queryMethod, String query, Object... arguments) {

    return "findByLastNameLike".equals(queryMethod.getName())
      ? query.trim()
          .replace("SELECT", "SELECT DISTINCT")
          .concat(" ORDER BY firstName ASC")
          .concat(String.format(" LIMIT %d", this.limit))
      : query;
  }
}
----
====

While this works, it possible to achieve the same affect just using the Spring Data _Repository_ convention and extensions
provided by _Spring Data for Pivotal GemFire_.  For instance, the same query could be defined as...

.CustomerRepository using the convention
====
[source,java]
----
interface CustomerRepository extends CrudRepository<Customer, Long> {

  @Limit(5)
  List<Customer> findDistinctByLastNameLikeOrderByFirstNameDesc(String lastName);

}
----
====

However, if you do not have control over the application `CustomerRepository` interface definition,
then the `QueryPostProcessor` (i.e. `OrderedLimitedCustomerByLastNameQueryPostProcessor`) is convenient.

If I want to ensure the `LoggingQueryPostProcessor` always comes after the other application-defined `QueryPostProcessors`
that I may have declared and registered in the Spring `ApplicationContext`, then I can set the `order` property
by overriding the `o.s.core.Ordered.getOrder()` method.

.Defining the `order` property
====
[source,java]
----
class LoggingQueryPostProcessor implements QueryPostProcessor<Repository, String> {

  @Override
  int getOrder() {
    return 1;
  }
}

class CustomerQueryPostProcessor implements QueryPostProcessor<CustomerRepository, String> {

  @Override
  int getOrder() {
    return 0;
  }
}
----
====

This ensures that I will always see the affects of the post processing applied by my other `QueryPostProcessors`
before my `LoggingQueryPostProcessor` logs the query.

You can define as many `QueryPostProcessors` in the Spring `ApplicationContext` as you like and apply them in any
order, to all or specific application _Repository_ interfaces, and be a granular as yuo like using the provided
arguments to the `postProcess(..)` method callback.
