Using Spring Data with GigaSpaces
This topic describes how to use Spring Data with GigaSpaces. It explains how to define query methods; you can use a specific query syntax as the method name, and the GigaSpaces repository proxy derives these methods into GigaSpaces queries.
For a full explanation of this mechanism, see the Spring Data Reference documentation.
After covering the basic usage of Spring Data with GigaSpaces, more advanced features are also described, such as Projection API, Change API, transactions, and Document storage.
Basic Usage
Defining Query Methods
The following code snippet is an example of a repository declaration with different methods:
@SpaceDocumentName(PersonDocument.TYPE_NAME)
public interface PersonDocumentRepository extends GigaspacesDocumentRepository<PersonDocument, String> {
// you can define simple queries
@Query("name = ?")
List<PersonDocument> findByName(String name);
// you can build complex queries
@Query("name = ? and age = ?")
List<PersonDocument> findByNameAndAge(String name, Integer age);
// you can query embedded properties
@Query("spouse.name = ?")
List<PersonDocument> findBySpouseName(String name);
// you can query any properties, even if they are not present in you wrapper
@Query("customField = ?")
List<PersonDocument> findByCustomField(String value);
// you can perform sorting using SQLQuery syntax
@Query("age = ? order by id asc")
List<PersonDocument> findByAgeSortedById(Integer age);
}
As you can see, different keywords can be used and combined to create desired conditions.
For a complete list of supported keywords, see Spring Data GigaSpaces Appendix.
The process of deriving query methods into GigaSpaces queries depends in large part on the query lookup strategy you choose for the repository. Spring Data GigaSpaces supports all common strategies.
The default strategy enables both deriving queries from method names and overriding them with custom defined queries. There are several ways to specify custom queries for a method.
@Query Annotation
The following example shows how to implement the @Query
annotation on the method:
public interface PersonRepository extends GigaspacesDocumentRepository<Person, String> {
@Query("name = ? order by name asc")
List<Person> findByNameOrdered(String name);
}
The syntax used for @Query
is similar to SQL queries.
For a full list of SQL query features, see the SQL Query API topic in the documentation website.
Importing Named Queries from an External Resource
You can import named queries from an external resource, For example, if you have a named-queries.properties
file in the classpath with the following content:
Person.findByNameOrdered=name = ? order by name asc
The query strings defined in the file are applied to methods with same names in PersonRepository
if you target named-queries.properties
in the configuration. In an XML-based configuration, the named-queries-location
attribute can be used as shown in the following example:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:gigaspaces-data="http://www.springframework.org/schema/data/gigaspaces"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:os-core="http://www.openspaces.org/schema/core"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/data/gigaspaces http://www.springframework.org/schema/data/gigaspaces/spring-data-gigaspaces-15.0.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
http://www.openspaces.org/schema/core http://www.openspaces.org/schema/15.0/core/openspaces-core.xsd">
<gigaspaces-data:repositories base-package="com.yourcompany.foo.bar" named-queries-location="classpath:named-queries.properties"/>
<!-- other configuration omitted -->
</beans>
Similarly, the namedQueriesLocation
annotation field can be used in a Java-based configuration, as shown here:
@Configuration
@EnableGigaspacesRepositories(value = "com.yourcompany.foo.bar", namedQueriesLocation = "classpath:named-queries.properties")
public class ContextConfiguration { // bean definitions omitted
}
Custom Methods
Custom methods can be added to repository interfaces. Spring Data allows you to provide custom repository code and still utilize basic CRUD Create, Read, Update, Delete. These terms describe the four essential operations for creating and managing persistent data elements, mainly in relational and NoSQL databases. features and query method functionality. To extend your repository, first define a separate interface with custom methods:
public interface PersonRepositoryCustom {
String customMethod();
}
Then you add an implementation for the defined interface:
java
public class PersonRepositoryCustomImpl implements PersonRepositoryCustom {
public String customMethod() {
// your custom implementation
}
}
Spring Data recognizes an Impl
suffix by default to look for custom method implementations.
The implementation itself doesn't depend on Spring Data, so you can inject other beans or property values using standard dependency injection. For example, you can inject GigaSpaces
and use it directly in your custom methods.
Next, apply the interface with custom methods to your repository declaration as shown below:
public interface PersonRepository extends GigaspacesRepository<Person, String>, PersonRepositoryCustom {
// query methods declarations are omitted
}
This combines basic CRUD methods and your custom functionality, and makes it available to the client application.
Spring Data looks for implementations of custom methods among all classes located under the base-package
attribute in XML or basePackages
in Java configuration. It searches for <custom interface name><suffix>
classes, where the suffix is Impl
by default. If your project conventions tell you to use another suffix for the implementations, you can specify it using the following syntax.
For XML configuration, use the repository-impl-postfix
attribute:
<gigaspaces-data:repositories
base-package="com.yourcompany.foo.bar"
repository-impl-postfix="FooBar"/>
For Java configuration, use repositoryImplementationPostfix
:
@Configuration
@EnableGigaspacesRepositories(value = "com.yourcompany.foo.bar", repositoryImplementationPostfix = "FooBar")
public class ContextConfiguration {
// bean definitions omitted
}
Another option is to manually put the implementation into the context and use a proper name for it.
In XML configuration it looks like this:
<gigaspaces-data:repositories base-package="com.yourcompany.foo.bar"/>
<bean id="personRepositoryImpl" class="...">
<!-- further configuration -->
</bean>
And similarly in Java-based configuration:
@Configuration
@EnableGigaspacesRepositories("com.yourcompany.foo.bar")
public class ContextConfiguration {
@Bean
public AnyClassHere personRepositoryImpl() {
// further configuration
}
// other bean definitions omitted
}
Advanced Usage
This section describes some of the more advanced features available through Spring Data GigaSpaces.
Projection API
Spring Data GigaSpaces supports Projection, which allows reading only certain properties for the objects (delta read). This approach reduces network overhead, memory consumption and CPU overhead due to decreased serialization time.
The GigaspacesRepository
interface provides basic find
methods extended with the Projection
argument. The following example demonstrates how the findOne
method can be used to select only the name
field from Person
:
@Service
public class PersonServiceImpl implements PersonService {
@Autowired
private PersonRepository repository;
public List<String> getAllNames() {
Iterable<Person> personList = repository.findAll(Projection.projections("name"));
// result processing ommited
}
}
If you are using Querydsl support, you can apply projection using QueryDslProjection
. This approach helps prevent runtime errors when the POJO Plain Old Java Object.
A regular Java object with no special restrictions other than those forced by the Java Language Specification and does not require any classpath. field is renamed but the projection fields aren't, because they are just strings.
You can also supply your query methods with Projection
by adding an additional argument to the method declaration:
public interface PersonRepository extends GigaspacesRepository<Person, String> {
List<Person> findByName(String name, Projection projection);
}
For more information about projection, see the Projection topic in the Querying the Space Using GigaSpaces API section of the documentation website.
Querydsl Support
The Querydsl framework lets you write type-safe queries in Java instead of using query strings. This provides several advantages, such as code completion in your IDE Integrated Development Environment. A software application that helps programmers develop software code efficiently. It increases developer productivity by combining capabilities such as software editing, building, testing, and packaging in an easy-to-use application. Example: DBeaver., and accessing domain types and properties in a type-safe manner (which reduces the probability of query syntax errors during runtime).
For more information about Querydsl, see the Querydsl website.
You need to perform several steps in order to start using Querydsl support with the GigaSpaces repositories.
To start using Querydsl support:
-
Use the repository as a
GigaspacesQueryDslPredicateExecutor
along withGigaspacesRepository
:public interface PersonRepository extends GigaspacesRepository<Person, String>, GigaspacesQueryDslPredicateExecutor<Person> { }
You define the type of data to be accessed with Querydsl.
-
Add the source processor to your Maven build (the pom.xml file) using the Maven Annotation Processing Tool plugin. This configuration calls
GigaspacespQueryDslAnnotationProcessor
before compiling your project sources. It looks for POJOs marked with@SpaceClass
annotation and generatesQ...
classes for them that allow you to build up QuerydslPredicate
s.<project> <build> <plugins> ... <plugin> <groupId>com.mysema.maven</groupId> <artifactId>apt-maven-plugin</artifactId> <version>1.1.3</version> <executions> <execution> <goals> <goal>process</goal> </goals> <configuration> <outputDirectory>target/generated-sources/java</outputDirectory> <processor>org.springframework.data.gigaspaces.querydsl.GigaspacesQueryDslAnnotationProcessor</processor> </configuration> </execution> </executions> </plugin> ... </plugins> </build> </project>
-
Before using these classes, you have to call this processor with
process-sources
Maven goal, or just callinstall
if you are already using it:mvn clean process-sources mvn clean install
-
Query your repository using Querydsl
Predicate
s:@Service public class PersonServiceImpl implements PersonService { @Autowired private PersonRepository repository; public Iterable<Person> getByAge(Integer minAge, Integer maxAge) { return repository.findAll( QPerson.person.name.isNotNull().and(QPerson.person.age.between(minAge, maxAge)) ); } }
For a full list of supported Predicate
methods, see the Spring Data GigaSpaces Appendix.
Change API
Spring Data GigaSpaces supports the GigaSpaces Change API, which allows updating existing objects in a Space by specifying only the required change instead of passing the entire updated object. This reduces network traffic between the client and the Space. It also can prevent having to read the existing object prior to the change operation, because the change operation can specify how to change the existing property without knowing its current value.
There are two ways you can use Change API within the GigaSpaces repositories.
Calling the Native Change API
The first option is to call the native Change API by accessing space()
in XGigaspacesRepository
. For this option, the GigaSpace.change
methods can be used along with ChangeSet
class .
For a full explanation and code examples, see the Change API section of the documentation website.
Using the Change API with Qerysdl Syntax
The second option is to use GigaspacesQueryDslPredicateExecutor.change
method built in Querydsl style. It accepts the QChangeSet
argument, which is just a ChangeSet
with Querydsl syntax:
@Service
public class PersonServiceImpl implements PersonService {
@Autowired
private PersonRepository repository;
public void increaseAgeByName(String name) {
repository.change(
QPerson.person.name.eq(name),
QChangeSet.changeSet().increment(QPerson.person.age, 1)
);
}
}
The Querydsl Support section above explains how to use APIs like the Change API with Querydsl syntax.
For a full list of supported Change API methods, see the Spring Data GigaSpaces Appendix.
Take Operations
Spring Data GigaSpaces supports take operations, which work in the same way as querying the Space, but returned objects are deleted from storage. With this approach there is no need for additional operations when you implement a pattern where consumers or workers are receiving tasks or messages.
A basic take operation can be performed using object IDs with take(...)
methods in the GigaspacesRepository
interface. More advanced querying is available in Querydsl style using the GigaspacesQueryDslPredicateExecutor
interface, which accepts Predicate
to retrieve one or multiple objects that match the query:
@Service
public class PersonServiceImpl implements PersonService {
@Autowired
private PersonRepository repository;
public Person takeByName(String name) {
return repository.takeOne(QPerson.person.name.eq(name));
}
}
Lease Time
Spring Data GigaSpaces supports defining a lease time for new objects in the repository, which limits the time an object lives in a Space. To use this feature, you can specify the lease time (in any time units) when saving with save(...)
methods. These overloaded methods return a special LeaseContext
object that allows you to track, renew and cancel the lease.
The idea behind a lease is fairly simple:
- When creating a resource, the requestor creates the resource with a limited life span.
- The grantor of the resource gives access for some period of time that is no longer than requested.
- The period of time that is actually granted is returned to the requestor as part of the Lease object.
- A holder of a lease can request that a Lease be renewed, or cancel the Lease at any time.
- Successfully renewing a lease extends the time period during which the lease is in effect.
- Canceling the lease drops the lease immediately.
For more information about leasing, see the Lease Time topic in the developer section of the documentation website.
Transactions
Spring Data GigaSpaces supports declarative Spring transactions based on OpenSpaces transaction managers. In order to apply transactional behavior, the transaction manager must be provided as a reference when constructing the GigaSpace
bean. For example (using the distributed transaction manager):
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:gigaspaces-data="http://www.springframework.org/schema/data/gigaspaces"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:os-core="http://www.openspaces.org/schema/core" xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/data/gigaspaces http://www.springframework.org/schema/data/gigaspaces/spring-data-gigaspaces.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
http://www.openspaces.org/schema/core http://www.openspaces.org/schema/15.0/core/openspaces-core.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd">
<context:annotation-config/>
<context:component-scan base-package="org.springframework.data.gigaspaces.examples.advanced.transaction"/>
<tx:annotation-driven transaction-manager="transactionManager"/>
<!-- Enables initialization for Gigaspaces Repositories:
all interfaces that extend the GigaspacesRepository will be initialized in the context
in this example only PersonRepository is initialized this way
-->
<gigaspaces-data:repositories base-package="org.springframework.data.gigaspaces.examples.advanced.transaction"/>
<os-core:embedded-space id="space" name="space"/>
<os-core:giga-space id="gigaSpace" space="space" tx-manager="transactionManager"/>
<os-core:distributed-tx-manager id="transactionManager"/>
</beans>
Now your service layer methods can be marked as @Transactional
:
@Service
public class PersonServiceImpl implements PersonService {
@Autowired
private PersonRepository personRepository;
@Transactional
public void transactionalMethod(Person person) {
...
}
}
For more information about transactions, see the Transactions section of the documentation website.
Document Storage Support
GigaSpaces's Document API exposes the Space as a Document Store. A document, which is represented by the class SpaceDocument
, is a collection of key-value pairs, where the keys are strings and the values are primitives, String
, Date
, other documents, or collections thereof. Most importantly, the Space is aware of the internal structure of a document, and so can index document properties at any nesting level and expose rich query semantics for retrieving documents.
When using Spring Data GigaSpaces you can declare one or more of your repositories to be a Document Repository. To do so, you must first add a schema definition of the document type to the Space configuration in context:
<os-core:embedded-space id="space" name="space">
<os-core:space-type type-name="Person">
<os-core:id property="id"/>
<os-core:routing The mechanism that is in charge of routing the objects into and out of the corresponding partitions. The routing is based on a designated attribute inside the objects that are written to the Space, called the Routing Index. property="age"/>
<os-core:basic-index path="name"/>
<os-core:extended-index path="birthday"/>
</os-core:space-type>
<!-- other document types declarations -->
</os-core:embedded-space>
Then, extend the GigaspacesDocumentRepository
interface (instead of the usual GigaspacesRepository
) and annotate it with @SpaceDocumentName
to wire it to the document descriptor declared above:
@SpaceDocumentName("Person")
public interface PersonDocumentRepository extends GigaspacesDocumentRepository<SpaceDocument, String> {
}
If you don't mark your Document Repository with a @SpaceDocumentName
annotation, context configuration will fail.
Now PersonDocumentRepository
has basic CRUD operations available for SpaceDocument
entities.
For more information about available document storage features, see the Document API topic on the documentation website.
While documents allow using a dynamic schema, they require give up Java's type-safety for working with typeless key-value pairs. Spring Data GigaSpaces supports extending the SpaceDocument
class to provide a type-safe wrapper for documents that is much easier to code with, while maintaining the dynamic schema. For example, you can declare a PersonDocument
wrapper:
public class PersonDocument extends SpaceDocument {
public static final String TYPE_NAME = "Person";
public static final String PROPERTY_ID = "id";
public static final String PROPERTY_AGE = "age";
// other properties omitted
public PersonDocument() {
super(TYPE_NAME);
}
public String getId() {
return super.getProperty(PROPERTY_ID);
}
public PersonDocument setId(String id) {
super.setProperty(PROPERTY_ID, id);
return this;
}
public Integer getAge() {
return super.getProperty(PROPERTY_AGE);
}
public PersonDocument setAge(Integer age) {
super.setProperty(PROPERTY_AGE, age);
return this;
}
// other properties accessors are omitted
}
Wrapper classes must have a parameter-less constructor.
To work with objects of a PersonDocument
class instead of SpaceDocument
, Space configuration must contain the declaration of the wrapper class
<os-core:embedded-space id="space" name="space">
<os-core:space-type type-name="Person">
<os-core:id property="id"/>
<os-core:routing property="age"/>
<os-core:basic-index path="name"/>
<os-core:extended-index path="birthday"/>
<os-core:document-class>com.yourcompany.foo.bar.PersonDocument</os-core:document-class>
</os-core:space-type>
<!-- other document types declarations -->
</os-core:embedded-space>
Now you can declare your Document Repository with the following syntax:
@SpaceDocumentName(PersonDocument.TYPE_NAME)
public interface PersonDocumentRepository extends GigaspacesDocumentRepository<PersonDocument, String> {
}
The domain class of PersonDocumentRepository
is now set to PersonDocument
instead of SpaceDocument
. Additionally, the type name for PersonDocument
is reused in the @SpaceDocumentName
annotation for the repository.
For more information about Space Documents, see the Extended Document topic in the Space Document section of the documentation website.
You can supply your Document Repository with query methods. Due to dynamic nature of SpaceDocument
, there is no way for Spring Data to automatically derive query method names into queries. The only possible way to declare a method is to use the @Query
annotation or load queries from external resources. The following is an example of a Document Repository supplied with search and sorting methods:
@SpaceDocumentName(PersonDocument.TYPE_NAME)
public interface PersonDocumentRepository extends GigaspacesDocumentRepository<PersonDocument, String> {
// you can define simple queries
@Query("name = ?")
List<PersonDocument> findByName(String name);
// you can build complex queries
@Query("name = ? and age = ?")
List<PersonDocument> findByNameAndAge(String name, Integer age);
// you can query embedded properties
@Query("spouse.name = ?")
List<PersonDocument> findBySpouseName(String name);
// you can query any properties, even if they are not present in you wrapper
@Query("customField = ?")
List<PersonDocument> findByCustomField(String value);
// you can perform sorting using SQLQuery syntax
@Query("age = ? order by id asc")
List<PersonDocument> findByAgeSortedById(Integer age);
}
You don't have to declare document properties to use them in queries, which allows dynamically adding and removing the properties.
Document Repositories don't support Querydsl syntax due to the dynamic nature of SpaceDocument
properties.