Advanced Usage
This section describes some of the more advanced features available through XAP Spring Data.
Projection API
The Spring Data XAP supports XAP Projection which allows to read only certain properties for the objects (delta read). This approach reduces network overhead, memory consumption and CPU overhead due to decreased serialization time.
The XapRepository
interface provides you with basic find
methods extended with the Projection
argument. The example demonstrates how the findOne
method can be used to select only 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 will let you avoid run-time errors when the POJO field is renamed and projection fields are not since they are just strings.
You can also supply your query methods with Projection
, just add an additional argument to the method declaration:
public interface PersonRepository extends XapRepository<Person, String> {
List<Person> findByName(String name, Projection projection);
}
To read more on projection refer to Projection reference.
Querydsl Support
The Querydsl
framework let’s you write type-safe queries in Java instead of using query strings. It gives you several advantages: code completion in your IDE, domain types and properties can be accessed in a type-safe manner which reduces the probability of query syntax errors during run-time. If you want to read more about Querydsl
, please, proceed to Querydsl website.
Several steps are needed to start using XAP Repositories Querydsl
support. First, use the repository as a XapQueryDslPredicateExecutor
along with XapRepository
:
public interface PersonRepository extends XapRepository<Person, String>, XapQueryDslPredicateExecutor<Person> {
}
Note that you define the type of data to be accessed with Querydsl.
Then, add the source processor to your maven build (pom.xml
) using Maven Annotation Processing Tool plugin:
<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.xap.querydsl.XapQueryDslAnnotationProcessor</processor>
</configuration>
</execution>
</executions>
</plugin>
...
</plugins>
</build>
</project>
This configuration will call XapQueryDslAnnotationProcessor
before compiling your project sources. It will look for POJOs marked with @SpaceClass
annotation and generate Q...
classes for them that allow you to build up Querydsl Predicate
s. Before using such classes, you have to call this processor with process-sources
maven goal, or just call install
if you are already using it:
mvn clean process-sources
mvn clean install
Now you can 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))
);
}
}
A full list of supported Predicate
methods can be found in Appendix B.
Change API
The Spring Data XAP supports XAP Change API allowing to update existing objects in Space by specifying only the required change instead of passing the entire updated object. It reduces network traffic between the client and the Space. It also can prevent the need of reading 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 possible ways you can use Change API within the Xap Repositories. The first option is to call the native Change API by accessing space()
in XapRepository
.
For that, the GigaSpace.change
methods along with ChangeSet
class can be used.
Full explanation and code examples can be found at Change API.
The second option would be to use XapQueryDslPredicateExecutor.change
method built in Querydsl
style. It accepts QChangeSet
argument that is literally 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)
);
}
}
To start using Querydsl Change API syntax, refer to Querydsl Support
The full list of supported change methods can be found in Appendix C.
Take Operations
The Spring Data XAP supports take operations that are the same as querying the space, but returned objects are deleted from the storage. This approach removes the need to perform additional operations when you implement a pattern where consumers or workers are receiving tasks or messages.
Basic take operation can be performed by object ids with take(...)
methods in XapRepository
interface. More advanced querying is available in Querydsl style within XapQueryDslPredicateExecutor
interface. Those accept 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 XAP Data comes with a support of defining lease time for new objects in the repository. The basic idea behind it is limiting the time an object lives in Space. To use this feature, you can specify the lease time (in any time units) when saving with save(...)
methods. These overloaded methods will return a special LeaseContext
object that allows you to track, renew and cancel the lease.
The essential 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 will then give access for some period of time that is no longer than that 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. * Cancelling the lease drops the lease immediately.
To read more about this feature refer to Lease Time.
Transactions
Spring Data XAP comes with a support of declarative Spring transactions based on OpenSpaces
transaction managers. In order to apply transactional behaviour, the transaction manager must be provided as a reference when constructing the GigaSpace
bean. For example (using the distributed transaction manager):
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xap-data="http://www.springframework.org/schema/data/xap"
xmlns:os-core="http://www.openspaces.org/schema/core"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns="http://www.springframework.org/schema/beans"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
http://www.springframework.org/schema/data/xap http://www.springframework.org/schema/data/xap/spring-xap-1.0.xsd
http://www.openspaces.org/schema/core http://www.openspaces.org/schema/10.1/core/openspaces-core.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-3.0.xsd">
<xap-data:repositories base-package="com.yourcompany.foo.bar"/>
<!-- Enables the detection on @Transactional annotations -->
<tx:annotation-driven transaction-manager="transactionManager"/>
<!-- Space declaration, nothing transaction-special here -->
<os-core:space-proxy id="space" name="space"/>
<!-- GigaSpace bean with provided transaction manager -->
<os-core:giga-space id="gigaSpace" space="space" tx-manager="transactionManager"/>
<!-- OpenSpaces distributed transaction manager -->
<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) {
...
}
}
To read more about transaction support refer to Transactions Reference.
Document Storage Support
The XAP Document API exposes the Space as 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 thus can index document properties at any nesting level and expose rich query semantics for retrieving documents.
While using Spring Data XAP you can declare one or more of your repositories to be a Document Repository. To do so, first, you have to add a schema definition of the document type into 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 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 XapDocumentRepository
interface (instead of usual XapRepository
) and annotate it with @SpaceDocumentName
to wire it to document descriptor declared above:
@SpaceDocumentName("Person")
public interface PersonDocumentRepository extends XapDocumentRepository<SpaceDocument, String> {
}
If you don’t mark your Document Repository with @SpaceDocumentName
annotation, context configuration will fail.
Now PersonDocumentRepository
will have basic CRUD operations available for SpaceDocument
entities. To read more on available document storage features, refer to Document API.
While documents allow using a dynamic schema, they force us to give up Java’s type-safety for working with type less key-value pairs. Spring Data XAP supports extending the SpaceDocument
class to provide a type-safe wrapper for documents which is much easier to code with, while maintaining the dynamic schema. As an example, let’s 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
}
Note that 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 we can declare our Document Repository with the next syntax:
@SpaceDocumentName(PersonDocument.TYPE_NAME)
public interface PersonDocumentRepository extends XapDocumentRepository<PersonDocument, String> {
}
Note that domain class of PersonDocumentRepository
is now set to PersonDocument
instead of SpaceDocument
. Also, type name for PersonDocument
is reused in @SpaceDocumentName
annotation for the repository.
For more information about the SpaceDocument
refer to Extended Document.
You can supply your Document Repository with query methods. But be aware that due to dynamic nature of SpaceDocument
there is no way for Spring Data to automatically derive query method names into queries. The only possibility to declare a method is to use @Query
annotation or load queries from external resources. Here is an example of Document Repository supplied with search and sorting methods:
@SpaceDocumentName(PersonDocument.TYPE_NAME)
public interface PersonDocumentRepository extends XapDocumentRepository<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 do not support Querydsl
syntax due to dynamic nature of SpaceDocument
properties.