Skip to content

Reference Documentation

Leonid Poliakov edited this page Mar 5, 2015 · 49 revisions

Spring Data XAP - Reference Documentation

Table of Contents

Preface

Requirements

Reference Documentation

  1. Document Structure

  2. Configuration

2.1 XAP Support

2.2 XAP Repositories

  1. Basic Usage

3.1 Query Methods

3.2 Custom Methods

  1. Advanced Usage

4.1 XAP Projection API

4.2 Querydsl Support

4.3 XAP Change API

4.4 XAP Take Operations

4.5 XAP Lease Time

4.6 XAP Transactions

4.7 XAP Document Storage Support

Other Resources

Appendix


Preface

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.


Requirements

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.


Reference Documentation

1. Document Structure

This part of the reference documentation explains the core functionality offered by Spring Data XAP.


2. Configuration


2.1 XAP Support

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>
Connecting to space using XML based metadata

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>
Connecting to space using Java based metadata

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();
    }
}
Other commonly used space configurations

In some projects you might want to apply other configurations to your space. There are a lot of options available to you.

Local cache

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.

Local view

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.

Space Persistence

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.

Event Processing

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.

Space Security

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.

Using native write and read operation

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, the getId() 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);
    }

    ...

}
Modeling your data

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:

Indexing

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.

Storage Types

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.

Exclusion

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.

Other Annotation-based Features

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.


2.2 XAP Repositories

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 and nullHandling in query expressions and Sort.

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.

Registering XAP repositories using XML-based metadata

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 under base-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.

Registering XAP repositories using Java-based metadata

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 with gigaspace attribute. An example can be found below.

Excluding custom interfaces from the search

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

    ...

}
Multi-space configuration

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();
    }
}

3. Basic Usage

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.


3.1 Query Methods

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
}

3.2 Custom Methods

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
}

4. Advanced Usage


4.1 XAP Projection API

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.


4.2 Querydsl Support

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 Predicates. 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 Predicates:

@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.


4.3 XAP 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 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.


4.4 XAP 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));
    }

}

To start using Querydsl take operations, refer to Querydsl Support


4.5 XAP 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 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.


4.6 XAP 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 on configuring XAP transactions and transaction managers, please, refer to Transactions Reference.


4.7 XAP 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.

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.


Other Resources

Useful links:


Appendix

Appendix A: Supported Query Keywords

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

Appendix B: Supported Querydsl Methods

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 the Regular Expression matches

Appendix C: Supported Change API methods

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

Appendix D: Unsupported operations

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:

Using IgnoreCase, Exists, IsNear and IsWithin keywords

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

}

Setting Sort to ignoreCase

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

Setting any NullHandling in Sort other than NATIVE

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

Using query derivation in XapDocumentRepository

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

}