[[gemfire-repositories]]
= {sdg-name} Repositories

{sdg-name} provides support for using the Spring Data Repository abstraction to easily persist entities into
{data-store-name} along with executing queries. A general introduction to the Repository programming model
is provided https://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, use the `<repositories/>` element from the {sdg-name} Data namespace,
as the following example shows:

.Bootstrap {sdg-name} Repositories in XML
====
[source,xml]
[subs="verbatim,attributes"]
----
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:gfe-data="{spring-data-access-schema-namespace}"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="
    http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd
    {spring-data-access-schema-namespace} {spring-data-access-schema-location}
">

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

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

The preceding configuration snippet looks for interfaces below the configured base package and creates Repository instances
for those interfaces backed by a https://docs.spring.io/spring-data/geode/docs/current/api/org/springframework/data/gemfire/repository/support/SimpleGemfireRepository.html[`SimpleGemFireRepository`].

IMPORTANT: The bootstrap process fails unless you have your application domain classes correctly mapped
to configured Regions.

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

Alternatively, many developers prefer to use Spring's {spring-framework-docs}/core.html#beans-java[Java-based container configuration].

Using this approach, you can bootstrap Spring Data Repositories by using the {sdg-acronym} `@EnableGemfireRepositories`
annotation, as the following example shows:

.Bootstrap {sdg-name} 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` lets you specify the package that contains all your application Repository classes by
specifying only one of your application Repository interface types. Consider creating a special no-op marker class
or interface in each package that serves no purpose other than to identify the location of application Repositories
referenced by this attribute.

In addition to the `basePackages and basePackageClasses` attributes, like Spring's
{spring-framework-javadoc}/org/springframework/context/annotation/ComponentScan.html[`@ComponentScan`] annotation,
the `@EnableGemfireRepositories` annotation provides include and exclude filters, based on Spring's
{spring-framework-javadoc}/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
{spring-framework-javadoc}/org/springframework/context/annotation/FilterType.html[`FilterType` Javadoc]
for more details.

The `@EnableGemfireRepositories` annotation also lets you specify the location of named OQL queries, which reside in
a Java `Properties` file, by 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 one or more {spring-data-commons-docs-html}/#repositories.custom-implementations[custom repository implementations].
This feature is commonly used to extend the Spring Data Repository infrastructure to implement a feature not provided by
the data store (for example, {sdg-acronym}).

One example of where custom repository implementations are needed with {data-store-name} is when performing joins.
Joins are not supported by {sdg-acronym} Repositories. With a {data-store-name} `PARTITION` Region, the join must be
performed on collocated `PARTITION` Regions, since {data-store-name} does not support "`distributed`" joins.
In addition, the Equi-Join OQL Query must be performed inside a {data-store-name} Function.
See https://gemfire91.docs.pivotal.io/geode/developing/partitioned_regions/join_query_partitioned_regions.html[here]
for more details on {data-store-name} _Equi-Join Queries_.

Many other aspects of the {sdg-acronym}'s Repository infrastructure extension may be 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

{sdg-name} Repositories enable the definition of query methods to easily execute {data-store-name} OQL queries
against the Region the managed entity maps to, as the following example shows:

.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 in the preceding example causes 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 returns all entities found, whereas the first query method expects a single result to be found.

If the supported keywords are not sufficient to declare and express your OQL query, or the method name becomes too
verbose, then you can annotate the query methods with `@Query` as shown on the third and fourth methods.

The following table gives brief samples of the supported keywords that you can use in query methods:

[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 {data-store-name}'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 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 convenient
and powerful abstraction.

To support {data-store-name}'s OQL Query language extensions and preserve portability across different data stores,
{sdg-name} adds support for OQL Query extensions by using Java annotations. These annotations are ignored by other
Spring Data Repository implementations (such as Spring Data JPA or Spring Data Redis) that do not have similar
query language features.

For instance, many data stores most likely do not implement {data-store-name}'s OQL `IMPORT` keyword. Implementing `IMPORT`
as an annotation (that is, `@Import`) rather than as part of the query method signature (specifically, the method 'name')
does not interfere with the parsing infrastructure when evaluating the query method name to construct another data store
language appropriate query.

Currently, the set of {data-store-name} OQL Query language extensions that are supported by {sdg-name} include:

[cols="1,2,2,2", options="header"]
.Supported {data-store-name} OQL extensions for Repository query methods
|===
| Keyword
| Annotation
| Description
| Arguments

| {x-data-store-docs}/developing/query_index/query_index_hints.html#topic_cfb_mxn_jq[HINT]
| `@Hint`
| OQL query index hints
| `String[]` (e.g. @Hint({ "IdIdx", "TxDateIdx" }))

| {x-data-store-docs}/developing/query_select/the_import_statement.html#concept_2E9F15B2FE9041238B54736103396BF7[IMPORT]
| `@Import`
| Qualify application-specific types.
| `String` (e.g. @Import("org.example.app.domain.Type"))

| {x-data-store-docs}/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)

| {x-data-store-docs}/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 {data-store-name} Region
along with a `CustomerRepository` and a query method to lookup `Customers` by last name, as follows:

.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);

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

The preceding example results in the following OQL Query:

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

{sdg-name}'s Repository extension 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`,
as follows:

.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);

}
----
====

The preceding 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`

The `@Limit(10)` annotation does not override the `LIMIT` explicitly defined in the raw query.
Also, the `@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 may possibly have
the same value for their reputation, which reduces the effectiveness of the index. Please choose indexes and other
optimizations wisely, as an improper or poorly chosen index can have the opposite effect on your performance because
of the overhead in maintaining the index. The `ReputationIdx` was used only to serve the purpose of the example.
====

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

Thanks to using the Spring Data Repository abstraction, the 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, {sdg-name} includes 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 that let you compose instances of `QueryPostProcessor` 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, the `QueryPostProcessor` interface implements the
{spring-framework-javadoc}/org/springframework/core/Ordered.html[`org.springframework.core.Ordered`] interface,
which is useful when multiple `QueryPostProcessors` are declared and registered in the Spring container 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 `T` extends the Spring Data Commons marker interface,
{spring-data-commons-javadoc}/org/springframework/data/repository/Repository.html[`org.springframework.data.repository.Repository`].
We discuss this further later in this section. All `QUERY` type parameter arguments in {sdg-name}'s case are of type
`java.lang.String`.

NOTE: It is useful to define the query as type `QUERY`, since this `QueryPostProcessor` interface may be ported to
Spring Data Commons and therefore must handle all forms of queries by different data stores (such as JPA, MongoDB,
or Redis).

You can 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, you might want to log all queries from all application Repository interface definitions. You could do so
by 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, logs 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, a `CustomerRepository`, as the following example shows:

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

  Customer findByAccountNumber(String accountNumber);

  List<Customer> findByLastNameLike(String lastName);

}
----
====

Then you could have typed the `LoggingQueryPostProcessor` specifically to the `CustomerRepository`, as follows:

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

As a result, only queries defined in the `CustomerRepository` interface, such as `findByAccountNumber`, are logged.

You might want to create a `QueryPostProcessor` for a specific query defined by a Repository query method. For example,
suppose you want to limit the OQL query generated from the `CustomerRepository.findByLastNameLike(:String)` query method
to only return five results along with ordering the `Customers` by `firstName`, in ascending order . To do so,
you can define a custom `QueryPostProcessor`, as the following example shows:

.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 the preceding example works, you can achieve the same effect by using the Spring Data Repository convention
provided by {sdg-name}.  For instance, the same query could be defined as follows:

.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` (that is, `OrderedLimitedCustomerByLastNameQueryPostProcessor`) is convenient.

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

.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 you always see the effects of the post processing applied by other `QueryPostProcessors`
before the `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 as granular as you like by using the provided arguments
to the `postProcess(..)` method callback.
