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 here.
To bootstrap Spring Data Repositories you use the <repositories/>
element from the GemFire Data namespace:
<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.acme.repository" />
</beans>
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.
The GemFire Repositories allow the definition of query methods to easily execute OQL Queries against the Region the managed entity is mapped to.
@Region("myRegion")
public class Person { … }
public interface PersonRepository extends CrudRepository<Person, Long> {
Person findByEmailAddress(String emailAddress);
Collection<Person> findByFirstname(String firstname);
@Query("SELECT * FROM /Person p WHERE p.firstname = $1")
Collection<Person> findByFirstnameAnnotated(String firstname);
@Query("SELECT * FROM /Person p WHERE p.firstname IN SET $1")
Collection<Person> findByFirstnamesAnnotated(Collection<String> firstnames);
}
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.
Keyword | Sample | Logical result |
---|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
(No keyword) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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:
Keyword | Annotation | Description | Arguments |
---|---|---|---|
|
OQL Query Index Hints |
|
|
|
Qualify application-specific types. |
|
|
|
Limit the returned query result set. |
|
|
|
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…
package ...;
import org.springframework.data.annotation.Id;
import org.springframework.data.gemfire.mapping.Region;
...
@Region("Customers")
public class Customer ... {
@Id
private Long id;
...
}
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 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…
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 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. |