-
Notifications
You must be signed in to change notification settings - Fork 3
Reference Documentation
2.1 XAP Support
2.2 XAP Repositories
3.1 Query Methods
3.2 Custom Methods
4.2 Querydsl Support
4.3 XAP Change API
4.5 XAP Lease Time
4.6 XAP Transactions
4.7 XAP Document Storage Support
The Spring Data XAP applies Spring Framework concepts to the development of applications that use XAP In-Memory Data Grid (IMDG) as a data store. We support Spring Data high-level abstractions to simplify configuration and development.
This document is the reference guide for Spring Data XAP. It explains basic and advanced configuration and usage of XAP IMDG features. It omits the concepts of Spring Data, XAP and XAP IMDG, assuming the reader already has a basic familiarity with the Spring Framework and XAP application development.
Spring Data XAP requires JDK level 6.0 or above, and Spring Framework 4.0.x and above.
In terms of data stores, XAP 10.1 or above is required.
Running application requires an active Data Grid to provide the Space for storage or an Embedded Space. Please, refer to Installation guide to get the platform installed. Running Data Grid and Embedding Space is explained later in XAP Support.
This part of the reference documentation explains the core functionality offered by Spring Data XAP.
-
Configuration section contains examples on how to configure space and repository in different ways.
- XAP Support - describes how to configure basic connection to space.
- XAP Repositories - introduces Spring Data repository configuration for XAP.
-
Basic Usage section introduces XAP support of basic Spring Data features.
- Query Methods - shows how to declare CRUD operations in XAP Repositories.
- Custom Methods - shows how to implement custom methods with XAP Repositories.
-
Advanced Usage section contains explanation and examples for querying support and specific XAP features.
- XAP Projection API - describes how to use Projection API integrated with Spring Repositories.
- Query DSL Support - explains the configuration and usage of Query DSL with XAP data store.
- XAP Change API - describes how to use Change API integrated with Spring Repositories.
- XAP Take Operations - shows take methods usage with simple amd Querydsl syntax.
- XAP Lease Time - shows how to use Lease Time.
- XAP Transactions - gives an example of using the Transactions.
- XAP Document Storage Support - introduces support of Document API.
The first task to handle while developing XAP application using Spring would be to configure a connection to the active space inside the Spring IoC container. This part of the document will show you how basic connection can be applied using XML or Java based Spring configurations.
To get started, first you need to get the space running: either configure an Embedded Space or set up a Service Grid and deploy the Data Grid on it.
To use the Embedded Space you don't need to start any additional processes on your environment.
To start the Data Grid refer to XAP Quick Start guide for an explanation on how to startup a space storage. Once installed, deploying Data Grid is typically a matter of executing the next commands from the GS_HOME/bin
folder:
*Windows*
gs-agent.bat
gs.bat deploy-space -cluster total_members=1,1 space
*Unix*
./gs-agent.sh
./gs.sh deploy-space -cluster total_members=1,1 space
Then in your project (assuming you build it with Maven) add the following to pom.xml
dependencies section:
<dependencies>
<!-- other dependency elements omitted -->
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-xap</artifactId>
<version>1.0.0.RELEASE</version>
</dependency>
</dependencies>
To use XAP Repository you need to provide a connection to space with an instance of GigaSpace
. Basic access can be easily configured with next Spring XML configuration:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:os-core="http://www.openspaces.org/schema/core"
xmlns="http://www.springframework.org/schema/beans"
xsi:schemaLocation="
http://www.openspaces.org/schema/core
http://www.openspaces.org/schema/10.1/core/openspaces-core.xsd
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.0.xsd">
<!-- A bean representing space proxy (requires active data grid with the same name) -->
<os-core:space-proxy id="space" name="space"/>
<!-- GigaSpace interface implementation used for SpaceClient injection -->
<os-core:giga-space id="gigaSpace" space="space"/>
</beans>
If you want to use an Embedded Space, the next configuration will suite your needs:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:os-core="http://www.openspaces.org/schema/core"
xmlns="http://www.springframework.org/schema/beans"
xsi:schemaLocation="
http://www.openspaces.org/schema/core
http://www.openspaces.org/schema/10.1/core/openspaces-core.xsd
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.0.xsd">
<!-- A bean representing an embedded space -->
<os-core:embedded-space id="space" name="space"/>
<!-- GigaSpace interface implementation used for SpaceClient injection -->
<os-core:giga-space id="gigaSpace" space="space"/>
</beans>
The same configuration can be achieved with next Java-based bean metadata:
@Configuration
public class ContextConfiguration {
/**
* Builds a space instance with settings that allow it to connect to the 'space'.
*/
@Bean
public GigaSpace space() {
UrlSpaceConfigurer urlSpaceConfigurer = new UrlSpaceConfigurer("jini://*/*/space");
return new GigaSpaceConfigurer(urlSpaceConfigurer).gigaSpace();
}
}
Or for the Embedded Space:
@Configuration
public class ContextConfiguration {
/**
* Builds a space instance with settings that allow it start the embedded space with name 'space'.
*/
@Bean
public GigaSpace space() {
UrlSpaceConfigurer urlSpaceConfigurer = new UrlSpaceConfigurer("/./space");
return new GigaSpaceConfigurer(urlSpaceConfigurer).gigaSpace();
}
}
In some projects you might want to apply other configurations to your space. There are a lot of options available to you.
A Local Cache is a Client Side Cache that maintains a subset of the master space’s data based on the client application’s recent activity. The local cache is created empty, and whenever the client application executes a query the local cache first tries to fulfill it from the cache, otherwise it executes it on the master space and caches the result locally for future queries.
To find out more about Local Cache and it's configuration, please, proceed to Local Cache Reference.
A Local View is a Client Side Cache that maintains a subset of the master space’s data, allowing the client to read distributed data without performing any remote calls or data serialization. Data is streamed into the client local view based on predefined criteria (a collection of SQLQuery objects) specified by the client when the local view is created.
To read more on Local View, please, proceed to Local View Reference.
A Space Persistence is a configuration where space data is persisted into permanent storage and retrieved from it.
Read more on persisting the space data here.
You might want to organize your system into event-driven structure. Using XAP there are several native features that will help you achieve your goals, among them: Notify Container, Polling Container, Archive Container.
XAP Security provides comprehensive support for securing your data, services, or both. XAP provides a set of authorities granting privileged access to data, and for performing operations on services.
To find out more about security configuration, please, refer to Security Guide.
GigaSpace
configured above can be used directly to perform interaction with space. To do so, you can simply inject GigaSpace
bean into your Repository classes. Let's see an example of such usage. First, here is an example of POJO class:
@SpaceClass
public class Person {
private Integer id;
private String name;
private Integer age;
public Person() {
}
@SpaceId(autoGenerate = true)
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
// getters and setters for other fields are omitted
}
Note that class is marked with
@SpaceClass
annotation - it allows Spring XAP to look for entities in your data model and automatically handle their structure. Also, thegetId()
method is marked with@SpaceId(autogenerate = true)
annotation - it will tell the space to handle ids automatically.
Now GigaSpace
can be injected and used directly in Repository layer:
@Repository
public class XapPersonRepository implements PersonRepository {
@Autowired
private GigaSpace space;
public void create(Person person) {
space.write(person);
}
public List<Person> findById(String personId) {
return space.readById(Person.class, personId);
}
...
}
Spring Data XAP comes with the transparent support of XAP native features. Among them, some additional configuration can be applied to your POJOs to boost up the performance, reduce memory usage or just ease the model understanding. When building data model using Spring Data XAP you might want to pay attention to the next features:
The most well-known data store function that allows to boost up common queries performance is index support. XAP provides several options here: basic, compound and unique indexes. All of these features can be applied by simply annotating POJO classes or their fields, e.g. with @SpaceIndex
or @CompoundSpaceIndex
annotations. Please, refer to Indexing for more details and examples of POJO classes.
You can define the form in which objects will be stored in Space either with annotations on each POJO in your model or with defining default Storage Type for the whole Space. This is done to save up time on serialization/de-serialization, reduce memory usage or to define schema that will change in time. Three Storage Types are available for POJOs: OBJECT
, BINARY
and COMPRESSED
. Again, whole configuration can be applied just by annotation your model classes. To read more on this feature, please, refer to Storage Types.
You can mark some POJO properties with @SpaceExclude
to disable writing their values to the Space. It will also affect Querydsl Q...
classes generation from POJOs - marked fields won't be available for querying in Querydsl style.
There are lots of other useful annotation-based configuration possibilities available to you. For the full list of them, please, refer to Annotation based Metadata.
This part of the document explains how to configure and start using XAP Repositories with Spring Data. While one can try to directly operate with GigaSpace
created from the previous section to perform read and write operations, it is generally easier to use Spring Data Repositories for the same purposes. This approach significantly reduces the amount of boilerplate code from your data-access layer as well as gives you more flexibility and cleaner code which is easy to read and support. GigaSpace
will be still available with space()
method at XapRepository
interface.
Spring Data XAP supports all Spring Data Commons configuration features like exclude filters, standalone configuration, manual wiring, etc. For more details on how to apply them, please, refer to Creating Repository Instances.
To start with handy Spring Data XAP features you will need to create your repository interface extending XapRepository
and tell Spring Container to look for such classes.
Spring Data XAP does not support
ignoreCase
andnullHandling
in query expressions andSort
.
An example of such user-defined repository with no additional functionality is given below:
public interface PersonRepository extends XapRepository<Person, String> {
}
Note that you define the type of data to be stored and the type of it's id.
While you can use Spring’s traditional <beans/>
XML namespace to register an instance of your repository implementing XapRepository
with the container, the XML can be quite verbose as it is general purpose. To simplify configuration, Spring Data XAP provides a dedicated XML namespace.
To enable Spring search for repositories, add the next configuration if you are using XML-based metadata:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xap-data="http://www.springframework.org/schema/data/xap"
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">
<xap-data:repositories base-package="com.yourcompany.foo.bar"/>
<!-- other configuration omitted -->
</beans>
Note that Spring Container will search for interfaces extending
XapRepository
in package and it's subpackages defined underbase-package
attribute.
Repositories will look for GigaSpace
bean by type in the context, if this behaviour is not overridden. If you have several space declarations, repositories will not be able to wire the space automatically. To define the space to be used by XAP Repositories, just add a gigaspace
attribute with a proper bean id. An example can be found below.
To achieve the same configuration with Java-based bean metadata, simply add @EnableXapRepositories
annotation to configuration class:
@Configuration
@EnableXapRepositories("com.yourcompany.foo.bar")
public class ContextConfiguration {
// bean definitions omitted
}
Note that
base-package
can be defined as a value of@EnableXapRepositories
annotation. Also,GigaSpace
bean will be automatically found in the context by type or can be explicitly wired withgigaspace
attribute. An example can be found below.
If you need to have an interface that won't be treated as a Repository by Spring Container, you can mark it with @NoRepositoryBean
annotation:
@NoRepositoryBean
public interface BaseRepository<T, ID extends Serializable> extends XapRepository<T, ID> {
// you can define methods that apply to all other repositories
T findByName(String name);
...
}
Sometimes it is required to have different groups of repositories to store and exchange data in different spaces. Configuration for such case will have several space declarations and several repository groups. For each group the space to use will be assigned using gigaspace
attribute referring to GigaSpace
bean id. Usually, groups of repositories will be placed in subpackages - it makes system structure clearer and eases configuration as well.
Here is an example of multi-space configuration in XML-based metadata:
<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="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">
<!-- Initializes repositories in .operational with operationalGSpace -->
<xap-data:repositories base-package="com.yourcompany.foo.bar.repository.operational" gigaspace="operationalGSpace"/>
<!-- Initializes repositories in .billing with billingGSpace -->
<xap-data:repositories base-package="com.yourcompany.foo.bar.repository.billing" gigaspace="billingGSpace"/>
<os-core:space-proxy id="billingSpace" name="billing"/>
<os-core:giga-space id="billingGSpace" space="billingSpace"/>
<os-core:embedded-space id="operationalSpace" name="operational"/>
<os-core:giga-space id="operationalGSpace" space="operationalSpace"/>
<!-- other configuration omitted -->
</beans>
In Java-based configuration you would have to separate groups of repositories into different sub-contexts:
@Configuration
@Import({OperationalRepositories.class, BillingRepositories.class})
public class ContextConfiguration {
/* other beans declaration omitted */
}
@Configuration
@EnableXapRepositories(basePackages = "com.yourcompany.foo.bar.repository.operational", gigaspace = "operationalGSpace")
class OperationalRepositories {
/**
* @return embedded operational space configuration
*/
@Bean
public GigaSpace operationalGSpace() {
return new GigaSpaceConfigurer(new UrlSpaceConfigurer("/./operational")).gigaSpace();
}
}
@Configuration
@EnableXapRepositories(basePackages = "com.yourcompany.foo.bar.repository.billing", gigaspace = "billingGSpace")
class BillingRepositories {
/**
* @return proxy billing space configuration
*/
@Bean
public GigaSpace billingGSpace() {
return new GigaSpaceConfigurer(new UrlSpaceConfigurer("jini://*/*/billing")).gigaSpace();
}
}
This section describes how to start using XAP Repositories by defining query methods. The basic concept is: user defines methods using a specific query syntax as method name, the XAP repository proxy then derives these methods into XAP queries. Full explanation of this mechanism can be found at Spring Data Reference. In this document only basic usage will be explained.
To start off, next declaration will allow application to search for objects by different field matches:
public interface PersonRepository extends XapRepository<Person, String> {
// you can query objects with exact field match
List<Person> findByName(String name);
List<Person> findByAge(Integer age);
// you can use ranged of search for number fields
List<Person> findByAgeBetween(Integer minAge, Integer maxAge);
// you can use boolean expressions to define complex conditions
List<Person> findByNameAndAge(String name, Integer age);
List<Person> findByNameOrAge(String name, Integer age);
}
As you can see, different keywords can be used and combined to create desired conditions. Full list of supported keywords can be found in Appendix A.
The process of deriving query methods into XAP Queries depends a lot on the query lookup strategy chosen for the repository. Spring XAP Data provides the support for 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 query for a method. First possibility is to apply @Query
annotation on the method:
public interface PersonRepository extends XapRepository<Person, String> {
@Query("name = ? order by name asc")
List<Person> findByNameOrdered(String name);
}
The syntax used for @Query
is similar to SQL queries. Refer to SQLQuery for the full list of possibilities.
Another way would be to import named queries from external resource. Let's say we have named-queries.properties
file in the classpath with next content:
Person.findByNameOrdered=name = ? order by name asc
The query strings defined in the file will be applied to methods with same names in PersonRepository
if we target named-queries.properties
in the configuration. In XML-based configuration the named-queries-location
attribute can be used:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xap-data="http://www.springframework.org/schema/data/xap"
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">
<xap-data:repositories base-package="com.yourcompany.foo.bar"
named-queries-location="classpath:named-queries.properties"/>
<!-- other configuration omitted -->
</beans>
Similarly, annotation field namedQueriesLocation
can be used in Java-based configuration:
@Configuration
@EnableXapRepositories(value = "com.yourcompany.foo.bar", namedQueriesLocation = "classpath:named-queries.properties")
public class ContextConfiguration {
// bean definitions omitted
}
It is often needed to add custom methods with your implementation to repository interfaces. Spring Data allows you to provide custom repository code and still utilize basic CRUD features and query method functionality. To extend your repository, you first define a separate interface with custom methods declarations:
public interface PersonRepositoryCustom {
String customMethod();
}
Then you add an implementation for the defined interface:
public class PersonRepositoryCustomImpl implements PersonRepositoryCustom {
public String customMethod() {
// your custom implementation
}
}
Note that Spring Data recognizes an
Impl
suffix by default to look for custom methods implementations.
The implementation itself does not depend on Spring Data, so you can inject other beans or property values into it using standard dependency injection. E.g. you could inject GigaSpaces
and use it directly in your custom methods.
The third step would be to apply interface with custom methods to your repository declaration:
public interface PersonRepository extends XapRepository<Person, String>, PersonRepositoryCustom {
// query methods declarations are ommited
}
This will combine basic CRUD methods and your custom functionality and make it available to clients.
So, how did it really work? Spring Data looks for implementations of custom method among all classes located under base-package
attribute in XML or basePackages
in Java configuration. It searches for <custom interface name><suffix>
classes, where suffix
is Impl
by default. If your project conventions tell you to use another suffix for the implementations, you can specify it with repository-impl-postfix
attribute in XML configuration:
<xap-data:repositories
base-package="com.yourcompany.foo.bar"
repository-impl-postfix="FooBar"/>
Or with repositoryImplementationPostfix
in Java configuration:
@Configuration
@EnableXapRepositories(value = "com.yourcompany.foo.bar", repositoryImplementationPostfix = "FooBar")
public class ContextConfiguration {
// bean definitions omitted
}
Another option would be to manually put the implementation into the context and use a proper name for it. In XML configuration it would look like this:
<xap-data:repositories base-package="com.yourcompany.foo.bar"/>
<bean id="personRepositoryImpl" class="...">
<!-- further configuration -->
</bean>
And similarly in Java-based configuration:
@Configuration
@EnableXapRepositories("com.yourcompany.foo.bar")
public class ContextConfiguration {
@Bean
public AnyClassHere personRepositoryImpl() {
// further configuration
}
// other bean definitions omitted
}
The Spring Data XAP supports XAP Projection which allows to read only certain properties for the objects (a.k.a. delta read). This approach reduces network overhead, garbage memory generation and CPU overhead due to decreased serialization time.
XapRepository
interface provides you with basic find
methods extended with Projection
argument. Next code demonstrates how 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
}
}
Note that if you are using Querydsl support, you can apply projection using
QueryDslProjection
. This approach will let you avoid run-time errors when 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 concepts, please, refer to Projection reference.
The Querydsl framework let's you write type-safe queries in Java instead of using good old 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, mark wanted 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 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))
);
}
}
Full list of supported Predicate
methods can be found in Appendix B.
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 Xap Repositories. The first option is to call native Change API by accessing space()
in XapRepository
. For that, 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
Full list of supported change methods can be found in Appendix C.
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));
}
}
To start using Querydsl take operations, refer to Querydsl Support
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 is reachable in space. To use this feature, you can specify 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, please, refer to Lease Time.
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 on configuring XAP transactions and transaction managers, please, refer to Transactions Reference.
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 toPersonDocument
instead ofSpaceDocument
. Also, type name forPersonDocument
is reused in@SpaceDocumentName
annotation for the repository.
If you want to read more on the concept of wrapping the SpaceDocument
, please, 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. Refer to Query methods to read more on possible find methods declarations. 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);
}
Note that 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.
Useful links:
The following table lists the keywords is supported by the Spring Data XAP repository query derivation mechanism:
Logical keyword | Keyword expressions |
---|---|
AND | And |
OR | Or |
AFTER | After, IsAfter |
BEFORE | Before, IsBefore |
CONTAINING | Containing, IsContaining, Contains |
BETWEEN | Between, IsBetween |
ENDING_WITH | EndingWith, IsEndingWith, EndsWith |
FALSE | False, IsFalse |
GREATER_THAN | GreaterThan, IsGreaterThan |
GREATER_THAN_EQUALS | GreaterThanEqual, IsGreaterThanEqual |
IN | In, IsIn |
IS | Is, Equals, (or no keyword) |
IS_NOT_NULL | NotNull, IsNotNull |
IS_NULL | Null, IsNull |
LESS_THAN | LessThan, IsLessThan |
LESS_THAN_EQUAL | LessThanEqual, IsLessThanEqual |
LIKE | Like, IsLike |
NOT | Not, IsNot |
NOT_IN | NotIn, IsNotIn |
NOT_LIKE | NotLike, IsNotLike |
REGEX | Regex, MatchesRegex, Matches |
STARTING_WITH | StartingWith, IsStartingWith, StartsWith |
TRUE | True, IsTrue |
Next keywords are not supported in XAP Repositories:
Logical keyword | Keyword expressions |
---|---|
EXISTS | Exists |
NEAR | Near, IsNear |
WITHIN | Within, IsWithin |
Next Predicate
methods are supported by Spring Data XAP to build up Querydsl queries:
- number comparison:
eq
,ne
,lt
,loe
,goe
,between
,notBetween
- string comparison:
like
,matches
,isEmpty
,isNotEmpty
,contains
,containsIgnoreCase
,endsWith
,endsWithIgnoreCase
,startsWith
,startsWithIgnoreCase
- other comparison:
isNull
,isNotNull
,in
,notIn
- complex queries:
and
,or
- embedded fields
Note that
contains
,startsWith
,endWith
and their...IgnoreCase
equivalents use theRegular Expression
matches
Next Change API methods are available while using Querydsl syntax (QChangeSet
class):
- field:
set
,unset
- numeric:
increment
,decrement
- collections and maps:
addToCollection
,addAllToCollection
,removeFromCollection
,putInMap
,removeFromMap
- lease:
lease
- custom change:
custom
Although we try to support each and every Spring Data feature, sometimes native implementation is not possible using Space as a data source. Instead of providing workarounds, which are often slow, we decided to mark some features as unsupported, among them are:
public interface PersonRepository extends XapRepository<Person, String> {
// these methods throw an UnsupportedOperationException when called
List<Person> findByNameIgnoreCase(String name);
List<Person> findByNameExists(boolean exists);
List<Person> findByAgeIsNear(Integer nearAge);
List<Person> findByAgeIsWithin(Integer minAge, Integer maxAge);
}
// Order.ignoreCase() is not supported
Sort sorting = new Sort(new Order(ASC, "id").ignoreCase());
// will throw an UnsupportedOperationException
personRepository.findByNameEquals("paul", new PageRequest(1, 2, sorting));
// NullHandling other than NATIVE is not supported
Sort sorting = new Sort(new Order(ASC, "id", NullHandling.NULLS_FIRST));
// will throw an UnsupportedOperationException
personRepository.findByNameEquals("paul", new PageRequest(1, 2, sorting));
@SpaceDocumentName("Person")
public interface DocumentQueries
extends XapDocumentRepository<SpaceDocument, String> {
@Query("name = ?")
List<SpaceDocument> findByName(String name);
// this declaration without @Query annotation
// or named query from external resource
// will throw UnsupportedOperationException during configuration
List<SpaceDocument> findByAge(Integer age);
}