Interacting with the Space

In this part of the tutorial we will demonstrate how to create a space and how you can interact with it. We will also demonstrate how you can improve your space search performance by using indexes and returning partial results.

Creating a Space

Let's create a space called "xapTutorialSpace' that is co-located within an application. This type of space is called embedded space.

Here is an example how you start an embedded space:

// Create the SpaceClosed Where GigaSpaces data is stored. It is the logical cache that holds data objects in memory and might also hold them in layered in tiering. Data is hosted from multiple SoRs, consolidated as a unified data model.
GigaSpace gigaSpace = new GigaSpaceConfigurer(new EmbeddedSpaceConfigurer("xapTutorialSpace")).gigaSpace();

This space we just created can also be accessed remotely from another JVMClosed Java Virtual Machine. A virtual machine that enables a computer to run Java programs as well as programs written in other languages that are also compiled to Java bytecode. by connecting with a SpaceProxyConfigurer. In order to do so you would use the following code:

GigaSpace gigaSpace = new GigaSpaceConfigurer(new SpaceProxyConfigurer("xapTutorialSpace")).gigaSpace();

You can configure the Space connection with several options.

For more information, see The Space URL.

When a client connects to a space, a proxy is created that holds a connection which implements the space API. All client interaction is performed through this proxy.

With data grid you can also create a Local Cache and a Local View.

  • Local Cache : This client side cache maintains any object used by the application. The cache data is loaded on demand (lazily), based on the client application's read operations.
  • Local View : This client side cache maintains a specific subset of the data. The subset is predefined by the user. The cache is populated when the client application is started. In both cases, updates are performed (objects are added/updated/removed) on the master space, the master space then propagates the changes to all relevant local views and caches.

These two scenarios are only applicable for remote clients.

For more information, see the Client-Side Caching page.

The Space Object

Data grid supports two types of objects that can interact with the Space, POJOs and Documents.


Any POJOClosed Plain Old Java Object. A regular Java object with no special restrictions other than those forced by the Java Language Specification and does not require any classpath. can be used to interact with the space as long it follows the Java BeansClosed A Java Bean is a reusable software component (class) that can be visually manipulated in builder tools. They adhere to a specific set of conventions and guidelines defined by Sun Microsystems (now Oracle). convention. The POJO needs to implement a default constructor, setters and getters for every property you want to store in the Space.

public class User {
  private Long id;
  private String name;
  private Double balance;
  private Double creditLimit;
  private EAccountStatus status;
  private Address address;
  private String[] comment;
  private Map<String, String> contacts;
  private List<Group> groups;
  private List<Integer> ratings;

  public User() {

  @SpaceId(autoGenerate = false)
  public Long getId() {
    return id;

The SpaceId

The space generates a unique identifier (UID) for every object in one of the following ways:

  • When a space object has no SpaceId attribute declared, the space auto-generates a UID for the object.
  • When a space object has an attribute which is declared as SpaceId and marked as auto-generate=false, the UID is generated based on the value of the ID attribute the user is setting.
  • When a space object has an attribute which is declared as SpaceId and marked as auto-generate=true, the UID is generated by the space and placed back into the attribute using the relevant setter method. In this case, the attribute must be of java.lang.String type.

Compound SpaceId

You might need to construct a space id that will be comprised from a user defined class rather than using a Numeric or String type field. In such a case your user defined class used as the SpaceId data type must implement the toString , hashCode and equals methods. The compound ID class must implement a toString method that return a unique String for each ID.

For more information, see the Space Object ID topic.

Defining Routing

Partitioning is used when the total number of objects is too big to be stored in a single space. In this case we will divide the data into several partitions. By designating an attribute on the space class as a partitioning key, the space proxy will then know to which partition a particular instance of the space class belongs to. The space proxy uses the entry's routingClosed The mechanism that is in charge of routing the objects into and out of the corresponding partitions. The routing is based on a designated attribute inside the objects that are written to the Space, called the Routing Index. attribute hash code to determine the corresponding partition for it.

The routing attribute can be explicitly set using the @SpaceRouting annotation for POJO entries or via the SpaceTypeDescriptorBuilder for document entries. If the routing attribute is not explicitly set, the space id attribute is used for routing. If the space id attribute is not defined, the first indexed attribute (alphabetically) is used for routing, otherwise the first attribute (alphabetically) is used for routing.

For more information, see the Routing In Partitioned Spaces page.

Space Document

The GigaSpaces document API exposes the space as Document Store. A document, which is represented by the class SpaceDocument, is essentially a collection of key-value pairs, where the keys are strings and the values are primitives, String, Date, other documents, or collections thereof. Unlike POJOs, which force users to design a fixed data schema (in the form of a class definition) and adhere to it, a document is much more dynamic, users can add and remove properties at runtime as necessary. A Document always belongs to a certain type, represented by the class SpaceTypeDescriptor.

To create a document we use a Map<String,Object> for its properties. The SpaceDocument object is instantiated by using the type name and properties. data grid provides a special implementation of a Hash Map called DocumentProperties that provides a fluent API.

Here is an example how you can create a SpaceDocument:

public SpaceDocument createDocumemt() {
     DocumentProperties properties = new DocumentProperties()
       .setProperty("CatalogNumber", "av-9876")
       .setProperty("Category", "Aviation")
       .setProperty("Name", "Jet Propelled Pogo Stick")
       .setProperty("Price", 19.99f)
            new String[] { "New", "Cool", "Pogo", "Jet" })
            new DocumentProperties()
              .setProperty("Manufacturer", "Acme")
              .setProperty("RequiresAssembly", true)
              .setProperty("NumberOfParts", 42));

       return new SpaceDocument("Product", properties);

In order to use the SpaceDocument, we need to register its schema first with the space:

public void registerProductType(GigaSpace space) {
     // Create type descriptor:
     SpaceTypeDescriptor typeDescriptor = new SpaceTypeDescriptorBuilder(
        .addPropertyIndex("Name", SpaceIndexType.EQUAL)
        .addPropertyIndex("Price", SpaceIndexType.ORDERED).create();
     // Register type:

Only properties with special roles like ID and Routing are part of the schema definition. These meta model settings cannot be changed without restarting the space or dropping the type, clearing all its instances and reintroducing it again.

It is possible to write a POJO to the space and read it back as a document, and vice versa. This scenario is useful when you want to read or modify POJO objects without loading the concrete java classes.

For more information, see the Interoperability topic.

Interacting with the Space

All space operations are relevant to both the POJO and Document.

Writing an Object to the Space

When writing an object to the space, the object is created in space if it does not exist. If it already exists in space it will be updated. This is the default behavior of the write operation.

public void writeUser() {
     User user = new User();
     user.setId(new Long(1));
     user.setName("John Smith");

     // Write the user to the space

It is also possible to write multiple objects in one operation to the space (batch mode). This can vastly improve the performance if you need to load many objects at once into the space.

Here is an example on how you write multiple objects to the space:

public void writeUsers() {
     User[] users = new User[2];
     users[0] = new User();
     users[0].setId(new Long(1));
     users[0].setName("John Doe");

     users[1] = new User();
     users[1].setId(new Long(2));
     users[1].setName("John Doe");


There are several options to override the default behavior of the write operation. You can change the lifetime of an object by supplying a LEASE to the operation. You can also change the modifier on the operation to change the behavior.

Here is an example:

public void writeOnlyWithLease() {
     User user = new User();
     user.setId(new Long(1));
     user.setName("John Smith");
     space.write(user, 0, 10000, WriteModifiers.WRITE_ONLY);

In this example, we are writing an object to the space with zero delay, 10 seconds to live and write only if the object does not already exist in the space. If the object already exists, an exception will be thrown.

Updating an Object in the Space

When you want to update only a couple of attributes on an object in space, you can use the change operation and update specific fields or even nested fields or modify collections and maps without having to supply the entire collection or map for the operation. With the following change operation example it is not necessary to read the object first from the space to update it. The Change API reduces a normal two step operation to a one step operation. This operation can vastly improve performance when you have an object with many attributes and you only need to update one or a couple of attributes.

public void ChangeSet() {
      User user = new User();
      user.setId(new Long(1));
      user.setName("John Doe");

      IdQuery<User> idQuery = new IdQuery<User>(User.class, new Long(1));
      ChangeResult<User> changeResult = space.change(idQuery,
        new ChangeSet().set("status", EAccountStatus.BLOCKED));

     if (changeResult.getNumberOfChangedEntries() == 0) {
        System.out.println("Entry does not exist");

There are several other change operations available; "increment', "decrement', "addToCollection', "removeFromCollection' etc.

Querying the Space

Now we are ready to query the space. data grid provides several ways to perform queries against the space:

  • Query by ID
  • Query by Template
  • Query by SQL

Query by ID

This is the simplest and fasted way to retrieve objects from the space.

Here is an example of a query by id:

public User findUserById() {
     return space.readById(User.class, new Long(1));

You can also perform a bulk read for multiple Id's

public User[] findUsersByIds() {
     ReadByIdsResult<User> result = space.readByIds(User.class, new Long[] {1l, 2l, 3l });
     return result.getResultsArray();

Query by Template

Template matching (match by example) is a simple way to query the space. The template is a POJO of the desired entry type, and the attributes which are set on the template (i.e. not null) are matched against the respective attributes of entries of the same type in the space. Attributes with null values are ignored (not matched).

The following examples assume the default constructor of the User class initializes all its attributes to null.

Read an entry of type User where the name is "John Doe':

public User findUserByTemplate() {
     User user = new User();
     user.setName("John Doe");
     return space.read(user);

You can also perform a bulk read with templates. In the example below will read all users that have a status of ACTIVE:

public User[] findUsersByTemplate() {
     User user = new User();
     return space.readMultiple(user);

Template Matching support inheritance relationships, so that entries of a sub-class are visible in the context of the super class, but not the other way around.

SQL Query

The SQLQuery class is used to query the space with an SQL-like syntax. The query statement includes only the WHERE statement part. An SQLQuery is composed from the class of entry to query and an expression in SQL syntax.

public User[] sqlFindUsersByName() {
     SQLQuery<User> query = new SQLQuery<User>(User.class,"name = 'John Doe'");
     return space.readMultiple(query);

public User[] sqlFindUsersByNameAndCreditLimit() {
     SQLQuery<User> query = new SQLQuery<User>(User.class,"name = 'John Doe' AND creditLimit > 1000");
     return space.readMultiple(query);

public User[] sqlFindUsersByNameAndIds() {
     SQLQuery<User> query = new SQLQuery<User>(User.class,"name = 'John Doe' AND id IN(1L,3L,5L)");
     return space.readMultiple(query);

For more information, see the SQL Query API topic.

Parameterized Queries

You can separate the values for the SQL criteria expression by placing a "?' symbol instead of the actual value in the expression. When executing the query, the conditions that includes "?' are replaced with the corresponding parameter values supplied via the setParameter method.

For example:

public User[] sqlParameterFindUsersByName() {
     SQLQuery<User> query = new SQLQuery<User>(User.class, "name = ?").setParameter(1, "John Doe");
     return space.readMultiple(query);

public User[] sqlParameterFindUsersByNameAndCreditLimit() {
     SQLQuery<User> query = new SQLQuery<User>(User.class,"name = ? AND creditLimit > ?");
     query.setParameter(1, "John Doe");
     query.setParameter(2, new Double(1000));
     return space.readMultiple(query);

Nested Property Queries

Many times a class has embedded classes as attributes. You can query for attributes within the embedded classes. Matching a nested attribute is done by specifying a Path which describes how to obtain its value. For example, our user class has an embedded attribute of an Address that has a zipCode attribute.

Here is an example how you can query the space for all users that have a zip code of "12345'.

public User[] sqlFindUsersByZipCode() {
     SQLQuery<User> query = new SQLQuery<User>(User.class,"address.zipCode = 12345");
     return space.readMultiple(query);

Nested Collections

It is possible to query embedded collections. Our user class has a collection groups that he belongs to. We can query the space for all users that belong to a certain group:

public User[] findUsersByGroup() {
   SQLQuery<User> sqlQuery = new SQLQuery<User>(User.class,"groups[*].id = 1L");
   return space.readMultiple(sqlQuery);

There are several additional query options available. For example you can query Nested Maps by key,query with Regular Expression, Enum attributes and others.

Query Returning Partial Results

In some cases when querying the space for objects only specific attributes of an objects are required and not the entire object (delta read). For that purpose the Projection API can be used where you can specify which attributes are of interest and the space will only populate these attributes with the actual data when the result is returned back to the user. This approach reduces network overhead and can vastly improve performance.

In this example below we are just interested in the name attribute of the user object:

public User[] findUsersByNameAndProjection() {
     SQLQuery<User> query = new SQLQuery<User>(User.class,"name = ?");
     query.setParameter(1, "John Doe");

     return space.readMultiple(query);

For more information, see the Projection topic.

Document Queries

You can also query the space for documents. Just like the POJO queries, you can use query by ID, template and SQLQuery.

Here are some examples how you can query the space for documents:

public SpaceDocument readProductById(GigaSpace gigaSpace) {
    return gigaSpace.readById(new IdQuery<SpaceDocument>("Product", "hw-1234"));

public SpaceDocument readProductByTemplate() {
     SpaceDocument template = new SpaceDocument("Product");
     template.setProperty("Name", "Jet Propelled Pogo Stick");
     return space.read(template);

public SpaceDocument[] readProductsBySQL() {
      SQLQuery<SpaceDocument> query = new SQLQuery<SpaceDocument>("Product","Price > ?");
      query.setParameter(1, 19.99f);
      return space.readMultiple(query);

For more information, see the Document API topic.

Geospatial Query

Spatial queries make use of geometry data types such as points, circles and polygons and these queries consider the spatial relationship between these geometries.

Suppose we want to write an application to locate nearby gas stations. First, we create a GasStation class which includes the location and address of the gas station:

And here is the corresponding java class:

import org.openspaces.spatial.shapes.Point;

import com.gigaspaces.annotation.pojo.SpaceClass;
import com.gigaspaces.annotation.pojo.SpaceId;

public class GasStation {

    private Long id;

    private Point location;

    public Point getLocation() {
        return location;

    public void setLocation(Point location) {
        this.location = location;
    public Long getId() {
        return id;

    public void setId(Long id) {
        this.id = id;

Let's write some gas stations into the Space:

    GigaSpace gigaSpace = new GigaSpaceConfigurer(new EmbeddedSpaceConfigurer("geoSpace")).gigaSpace();

    GasStation gs = new GasStation();
    gs.setId(new Long(1));
    gs.setLocation(ShapeFactory.point(10.0d, 10.0d));


Next, we can query for a gas station within a certain radius of our location:

    Point p = ShapeFactory.point(7.5d, 7.5d);

    SQLQuery<GasStation> query = new SQLQuery<GasStation>(GasStation.class, "location spatial:within ?")
                .setParameter(1, ShapeFactory.circle(p, 4.5d));
    GasStation station = gigaSpace.read(query);

    if (station != null) {
        System.out.println("Found a GasStation :" + station);


Data grid supports the following shapes:

Shape Description
Point A point, denoted by X and Y coordinates.
LineString A finite sequence of one or more consecutive line segments.
Circle A circle, denoted by a point and a radius.
Rectangle A rectangle aligned with the axis (for non-aligned rectangles use Polygon).
Polygon A finite sequence of consecutive line segments which denotes a bounded area.


Spatial queries are available through the spatial: extension to the SQL query syntax. The following operations are supported:

Query Description
shape1 spatial:intersects shape2 The intersection between shape1 and shape 2 is not empty (i.e. some or all of shape1 overlaps some or all of shape2).
shape1 spatial:within shape2 shape1 is within (contained in) shape2, boundaries inclusive.
shape1 spatial:contains shape2 shape1 contains shape2, boundaries inclusive.

For more information, see the Geospatial Queries topic.

Removing Objects from the Space

To remove objects from a space you can use the take or the clear operation.

Take Operation

The take operation returns an object and removes it from the space. Data grid provides several options for the take operation:

  • Take by ID
  • Take by template
  • Take by SQLQuery
  • Take multiple

Here are some examples:

public User takeUserById() {
   return space.takeById(User.class, 1L);

public User takeUserByTemplate() {
   User template = new User();
   template.setName("John Doe");
   return space.take(template);

public User[] takeUsersBySQL() {
     SQLQuery<User> query = new SQLQuery<User>(User.class,"status = ?");
     query.setParameter(1, EAccountStatus.BLOCKED);
   return space.takeMultiple(query);

Clear Operation

The clear operation removes objects from a space without returning them. Here are some examples:

public void clearUserByTemplate() {
     User template = new User();

public void clearUserBySQL() {
     SQLQuery<User> query = new SQLQuery<User>(User.class, "name = ?");
     query.setParameter(1, "John Doe");

// clear all objects in space
public void clearAllObjectInSpace() {

For more information, see the Operations topic.


The Aggregators allow you to perform the entire aggregation activity at the space side avoiding any data retrieval back to the client side. Only the result of each aggregation activity performed with each partition is returned back to the client side where all the results are reduced and returned to the client application.

import static org.openspaces.extensions.QueryExtension.*;
SQLQuery<Employee> query = new SQLQuery<Employee>(Employee.class,"country=? OR country=? ");
query.setParameter(1, "UK");
query.setParameter(2, "U.S.A");

// retrieve the maximum value stored in the field "age"
Number maxAgeInSpace = max(space, query, "age");
/// retrieve the minimum value stored in the field "age"
Number minAgeInSpace = min(space, query, "age");
// Sum the "age" field on all space objects.
Number combinedAgeInSpace = sum(space, query, "age");
// Sum's the "age" field on all space objects then divides by the number of space objects.
Double averageAge = average(space, query, "age");
// Retrieve the space object with the highest value for the field "age".
Person oldestPersonInSpace = maxEntry(space, query, "age");
/// Retrieve the space object with the lowest value for the field "age".
Person youngestPersonInSpace = minEntry(space, query, "age");

Data grid also supports, Compound, Embedded Fields and Group Aggregation.

For more information, see the Aggregators topic.


To improve performance, it is possible to index one or more attributes for an object. The space maintains additional data for indexed attributes, which shortens the time required to determine a match resulting in improved performance. However, indexes consume more resources and impacts the write operations performance.


By default, an attribute's index is inherited in sub classes (i.e. if an attribute is indexed in a super class, it is also indexed in a sub class). If you need to change the index type of an attribute in a subclass you can override the attribute and annotate it with @SpaceIndex using the requested index type (to disable indexing use NONE).

Space Index

There are three Space index types provided:

  • EQUAL - performs equality matching (equal to/not equal to).
  • ORDERED - performs ordered matching (bigger than/less than).
  • EQUAL_AND_ORDERED - performs both equality and ordered matching, and uses a larger memory footprint than the other indexing types.

Here is an example how you can define indexes:

public class User {

    private Long id;
    private String name;
    private Double balance;
    private Double creditLimit;
    private EAccountStatus status;
    private Address address;
    private Map<String, String> contacts;

    public User() {

    @SpaceId(autoGenerate = false)
    public Long getId() {
        return id;

    @SpaceIndex(type = SpaceIndexType.EQUAL)
    public String getName() {
        return name;

    @SpaceIndex(type = SpaceIndexType.ORDERED)
    public Double getCreditLimit() {
        return creditLimit;

Compound Indexing

A Compound Index is a space index composed from several attributes or nested attributes. Each attribute of a compound index is called a segment and each segment is described by its path.

Here is an example of a compound index:

@CompoundSpaceIndexes({ @CompoundSpaceIndex(paths = { "name", "creditLimit" }) })
public class User {
     private Long id;
     private String name;
     private Double balance;
     private Double creditLimit;
     private EAccountStatus status;
     private Address address;
     private String[] comment;

// Here is a query that will use this index
SQLQuery<User> query = new SQLQuery<User>(User.class,"name = 'John Doe' AND creditLimit > 1000");

There are several additional indexing options available. For example you can index nested attributes, Nested Maps, Collections, nested attributes within a Collection, free text search and others.

For more information, see the Indexing section of the Developer guide.

Best Practice

When you code your space classes make sure:

  • there are indexes for all relevant attributes including nested attributes you use for queries
  • numeric attribute queried with between / greater / less than should have an ordered index.
  • compound indexes should be used for attributes queried using AND query
  • space classes have empty no arg constructor
  • all nested classes are serializable
  • do not use int, long, etc. integer attributes, instead use Long.
  • when possible use writeMultiple.
  • use projection for read/readMultiple
  • use clear for data removal and not take or takeMultiple
  • no huge collections with many items
  • use change api instead of update, especially if collections are used.

Other Data Access APIs

Data grid provides a JDBCClosed Java DataBase Connectivity. This is an application programming interface (API) for the Java programming language, which defines how a client may access a database. Driver, JPA API, MAP and Memcached APIs.

For more information, see the Data Access APIs section of the Developer guide.

Spring Integration

All data grid components can be wired and configured with the application using corresponding Spring Beans.

The GigaSpaces Spring Integration supports:

  • Spring Automatic Transaction Demarcation
  • Spring Data
  • Spring JMS
  • Spring JPA
  • Spring Hibernate
  • Spring Remoting
  • String Batch
  • Spring Security
  • Mule

Lets look at a Spring configuration file that represents the creation of an embedded space:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context"
    xmlns:os-core="http://www.openspaces.org/schema/core" xmlns:os-events="http://www.openspaces.org/schema/events"
   http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
   http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
   http://www.openspaces.org/schema/core http://www.openspaces.org/schema/17.0.0/core/openspaces-core.xsd">

    <!-- Scan the packages for annotations / -->
    <context:component-scan base-package="xap" />

    <!-- Enables to configure Spring beans through annotations -->
    <context:annotation-config />

    <!-- A bean representing a space (an IJSpace implementation) -->
    <os-core:embedded-space id="space" space-name="tutorialSpace" />

    <!-- Define the GigaSpace instance that the application will use to access the space -->
    <os-core:giga-space id="xapTutorialSpace" space="space"/>

And here is the code to access the Spring bean within your application:

public void findSpace()  {
    FileSystemXmlApplicationContext context = new FileSystemXmlApplicationContext(

    GigaSpace space = (GigaSpace) context.getBean("xapTutorialSpace");

In the following parts of this tutorial we will introduce you the different schemas that support the data grid Spring integration. We will also use the Annotations to configure and inject components.