Apache Ignite Integrations

The Apache Ignite Integrations Developer Hub

Welcome to the Apache Ignite Integrations developer hub. You'll find comprehensive guides and documentation to help you start working with Apache Ignite Integrations as quickly as possible, as well as support if you get stuck. Let's jump right in!

Get Started    

Automatic Persistence

Automatically read-through and write-through your domain model to and from a database.

Overview

Ignite ships with its own database schema mapping wizard which provides automatic support for integrating with persistence stores. This utility automatically connects to the underlying database and generates all the required XML OR-mapping configuration and Java domain model POJOs.

Ignite also ships with org.apache.ignite.cache.store.jdbc.CacheJdbcPojoStore, which is out-of-the-box JDBC implementation of the IgniteCacheStore interface, and automatically handles all the write-through and read-through logic.

Database Schema Import

To start the wizard for generating database schema mapping, execute script: bin/ignite-schema-import.{sh|bat}.

This command will bring up a UI wizard which will take you through a couple of screens.

Connect To Database

The first screen will ask you for database connectivity settings. Note that JDBC drivers are not supplied with the utility and should be provided separately.

Generate XML Configuration and POJOs

The second screen allows to map database tables to domain model classes and automatically generates XML configurations and POJOs.

Generated Artifacts

The utility generates the following artifacts:

  • Java POJO key and value classes.
  • XML CacheTypeMetadata configuration.
  • CacheConfig.java (alternative to XML).

After you exit from the wizard, you should

  1. Copy generated POJO java classes to you project source folder.
  2. Copy XML declaration of CacheJdbcPojoStoreFactory to your Ignite XML configuration file under appropriate CacheConfiguration root.
  3. Use CacheConfig.java in your Ignite initialization logic.
/**
 * PersonKey definition.
 *
 * Code generated by Apache Ignite Schema Import utility: 03/03/2015.
 */
public class PersonKey implements Serializable {
    /** */
    private static final long serialVersionUID = 0L;

    /** Value for id. */
    private int id;

    /**
     * Gets id.
     *
     * @return Value for id.
     */
    public int getId() {
        return id;
    }

    /**
     * Sets id.
     *
     * @param id New value for id.
     */
    public void setId(int id) {
        this.id = id;
    }

    /** {@inheritDoc} */
    @Override public boolean equals(Object o) {
        if (this == o)
            return true;

        if (!(o instanceof PersonKey))
            return false;

        PersonKey that = (PersonKey)o;

        if (id != that.id)
            return false;

        return true;
    }

    /** {@inheritDoc} */
    @Override public int hashCode() {
        int res = id;

        return res;
    }

    /** {@inheritDoc} */
    @Override public String toString() {
        return "PersonKey [id=" + id +
            "]";
    }
}
/**
 * Person definition.
 *
 * Code generated by Apache Ignite Schema Import utility: 10/10/2016.
 */
public class Person implements Serializable {
    /** */
    private static final long serialVersionUID = 0L;

    /** Value for id. */
    private int id;

    /** Value for firstName. */
    private String firstName;

    /** Value for lastName. */
    private String lastName;

    /** Value for salary. */
    private double salary;

    /**
     * Empty constructor.
     */
    public Person() {
        // No-op.
    }

    /**
     * Full constructor.
     */
    public Person(
        int id,
        String firstName,
        String lastName,
        double salary
    ) {
        this.id = id;
        this.firstName = firstName;
        this.lastName = lastName;
        this.salary = salary;
    }

    /**
     * Gets id.
     *
     * @return Value for id.
     */
    public int getId() {
        return id;
    }

    /**
     * Sets id.
     *
     * @param id New value for id.
     */
    public void setId(int id) {
        this.id = id;
    }

    /**
     * Gets firstName.
     *
     * @return Value for firstName.
     */
    public String getFirstName() {
        return firstName;
    }

    /**
     * Sets firstName.
     *
     * @param firstName New value for firstName.
     */
    public void setFirstName(String firstName) {
        this.firstName = firstName;
    }

    /**
     * Gets lastName.
     *
     * @return Value for lastName.
     */
    public String getLastName() {
        return lastName;
    }

    /**
     * Sets lastName.
     *
     * @param lastName New value for lastName.
     */
    public void setLastName(String lastName) {
        this.lastName = lastName;
    }

    /**
     * Gets salary.
     *
     * @return Value for salary.
     */
    public double getSalary() {
        return salary;
    }

    /**
     * Sets salary.
     *
     * @param salary New value for salary.
     */
    public void setSalary(double salary) {
        this.salary = salary;
    }

    /** {@inheritDoc} */
    @Override public boolean equals(Object o) {
        if (this == o)
            return true;

        if (!(o instanceof Person))
            return false;

        Person that = (Person)o;

        if (id != that.id)
            return false;

        if (firstName != null ? !firstName.equals(that.firstName) : that.firstName != null)
            return false;

        if (lastName != null ? !lastName.equals(that.lastName) : that.lastName != null)
            return false;

        if (Double.compare(salary, that.salary) != 0)
            return false;

        return true;
    }

    /** {@inheritDoc} */
    @Override public int hashCode() {
        int res = id;

        res = 31 * res + (firstName != null ? firstName.hashCode() : 0);

        res = 31 * res + (lastName != null ? lastName.hashCode() : 0);

        long ig_hash_temp = Double.doubleToLongBits(salary);

        res = 31 * res + (int)(ig_hash_temp ^ (ig_hash_temp >>> 32));

        return res;
    }

    /** {@inheritDoc} */
    @Override public String toString() {
        return "Person [id=" + id +
            ", firstName=" + firstName +
            ", lastName=" + lastName +
            ", salary=" + salary +
            "]";
    }
}
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
     xmlns:util="http://www.springframework.org/schema/util"
     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
     xsi:schemaLocation="http://www.springframework.org/schema/beans
               http://www.springframework.org/schema/beans/spring-beans.xsd
               http://www.springframework.org/schema/util
               http://www.springframework.org/schema/util/spring-util.xsd">
  <bean class="org.apache.ignite.cache.store.jdbc.CacheJdbcPojoStoreFactory">
    <property name="types">
      <list>
        <bean class="org.apache.ignite.cache.store.jdbc.JdbcType">
          <property name="databaseSchema" value="PUBLIC"/>
          <property name="databaseTable" value="PERSON"/>
          <property name="keyType" value="sample.PersonKey"/>
          <property name="valueType" value="sample.Person"/>
          <property name="keyFields">
            <list>
              <bean class="org.apache.ignite.cache.store.jdbc.JdbcTypeField">
                <property name="databaseFieldType">
                  <util:constant static-field="java.sql.Types.INTEGER"/>
                </property>
                <property name="databaseFieldName" value="ID"/>
                <property name="javaFieldType" value="int"/>
                <property name="javaFieldName" value="id"/>
              </bean>
            </list>
          </property>
          <property name="valueFields">
            <list>
              <bean class="org.apache.ignite.cache.store.jdbc.JdbcTypeField">
                <property name="databaseFieldType">
                  <util:constant static-field="java.sql.Types.INTEGER"/>
                </property>
                <property name="databaseFieldName" value="ID"/>
                <property name="javaFieldType" value="int"/>
                <property name="javaFieldName" value="id"/>
              </bean>
              <bean class="org.apache.ignite.cache.store.jdbc.JdbcTypeField">
                <property name="databaseFieldType">
                  <util:constant static-field="java.sql.Types.VARCHAR"/>
                </property>
                <property name="databaseFieldName" value="FIRST_NAME"/>
                <property name="javaFieldType" value="java.lang.String"/>
                <property name="javaFieldName" value="firstName"/>
              </bean>
              <bean class="org.apache.ignite.cache.store.jdbc.JdbcTypeField">
                <property name="databaseFieldType">
                  <util:constant static-field="java.sql.Types.VARCHAR"/>
                </property>
                <property name="databaseFieldName" value="LAST_NAME"/>
                <property name="javaFieldType" value="java.lang.String"/>
                <property name="javaFieldName" value="lastName"/>
              </bean>
              <bean class="org.apache.ignite.cache.store.jdbc.JdbcTypeField">
                <property name="databaseFieldType">
                  <util:constant static-field="java.sql.Types.DOUBLE"/>
                </property>
                <property name="databaseFieldName" value="SALARY"/>
                <property name="javaFieldType" value="double"/>
                <property name="javaFieldName" value="salary"/>
              </bean>
            </list>
          </property>
        </bean>
      </list>
    </property>
  </bean>
  <bean class="org.apache.ignite.cache.QueryEntity">
    <property name="keyType" value="sample.PersonKey"/>
    <property name="valueType" value="sample.Person"/>
    <property name="fields">
      <util:map map-class="java.util.LinkedHashMap">
        <entry key="id" value="java.lang.Integer"/>
        <entry key="firstName" value="java.lang.String"/>
        <entry key="lastName" value="java.lang.String"/>
        <entry key="salary" value="java.lang.Double"/>
      </util:map>
    </property>
    <property name="aliases">
      <map>
        <entry key="firstName" value="FIRST_NAME"/>
        <entry key="lastName" value="LAST_NAME"/>
      </map>
    </property>
  </bean>
</beans>
/**
 * CacheConfig definition.
 *
 * Code generated by Apache Ignite Schema Import utility: 10/10/2016.
 */
public class CacheConfig {
    /**
     * Create JDBC type for PERSON.
     *
     * @param cacheName Cache name.
     * @return Configured JDBC type.
     */
    private static JdbcType jdbcTypePerson(String cacheName) {
        JdbcType jdbcType = new JdbcType();

        jdbcType.setCacheName(cacheName);
        jdbcType.setDatabaseSchema("PUBLIC");
        jdbcType.setDatabaseTable("PERSON");
        jdbcType.setKeyType("sample.PersonKey");
        jdbcType.setValueType("sample.Person");

        // Key fields for PERSON.
        Collection<JdbcTypeField> keys = new ArrayList<>();
        keys.add(new JdbcTypeField(Types.INTEGER, "ID", int.class, "id"));
        jdbcType.setKeyFields(keys.toArray(new JdbcTypeField[keys.size()]));

        // Value fields for PERSON.
        Collection<JdbcTypeField> vals = new ArrayList<>();
        vals.add(new JdbcTypeField(Types.INTEGER, "ID", int.class, "id"));
        vals.add(new JdbcTypeField(Types.VARCHAR, "FIRST_NAME", String.class, "firstName"));
        vals.add(new JdbcTypeField(Types.VARCHAR, "LAST_NAME", String.class, "lastName"));
        vals.add(new JdbcTypeField(Types.DOUBLE, "SALARY", double.class, "salary"));
        jdbcType.setValueFields(vals.toArray(new JdbcTypeField[vals.size()]));

        return jdbcType;
    }

    /**
     * Create SQL Query descriptor for PERSON.
     *
     * @return Configured query entity.
     */
    private static QueryEntity queryEntityPerson() {
        QueryEntity qryEntity = new QueryEntity();

        qryEntity.setKeyType("sample.PersonKey");
        qryEntity.setValueType("sample.Person");

        // Query fields for PERSON.
        LinkedHashMap<String, String> fields = new LinkedHashMap<>();

        fields.put("id", "java.lang.Integer");
        fields.put("firstName", "java.lang.String");
        fields.put("lastName", "java.lang.String");
        fields.put("salary", "java.lang.Double");

        qryEntity.setFields(fields);

        // Aliases for fields.
        Map<String, String> aliases = new HashMap<>();

        aliases.put("firstName", "FIRST_NAME");
        aliases.put("lastName", "LAST_NAME");

        qryEntity.setAliases(aliases);

        return qryEntity;
    }

    /**
     * Configure cache.
     *
     * @param cacheName Cache name.
     * @param storeFactory Cache store factory.
     * @return Cache configuration.
     */
    public static <K, V> CacheConfiguration<K, V> cache(String cacheName, CacheJdbcPojoStoreFactory<K, V> storeFactory) {
        if (storeFactory == null)
             throw new IllegalArgumentException("Cache store factory cannot be null.");

        CacheConfiguration<K, V> ccfg = new CacheConfiguration<>(cacheName);

        ccfg.setCacheStoreFactory(storeFactory);
        ccfg.setReadThrough(true);
        ccfg.setWriteThrough(true);

        // Configure JDBC types.
        Collection<JdbcType> jdbcTypes = new ArrayList<>();

        jdbcTypes.add(jdbcTypePerson(cacheName));

        storeFactory.setTypes(jdbcTypes.toArray(new JdbcType[jdbcTypes.size()]));

        // Configure query entities.
        Collection<QueryEntity> qryEntities = new ArrayList<>();

        qryEntities.add(queryEntityPerson());

        ccfg.setQueryEntities(qryEntities);

        return ccfg;
    }
}

CacheJdbcPojoStore

After you generate XML and POJOs in wizard and copy all necessary stuff into your project you could use CacheJdbcPojoStore to load data from database into cache and write data from cache into database.

First you need to properly declare store in configuration:

<!-- Sample data source -->
<bean id= "sampleDataSource" class="org.h2.jdbcx.JdbcDataSource">
 <property name="url" value="jdbc:h2:tcp://localhost/mem:ExampleDb"/>
 <property name="user" value="sa"/>
</bean>

<bean class="org.apache.ignite.configuration.IgniteConfiguration">
...
 <!-- Cache configuration. -->
 <property name="cacheConfiguration">
  <list>
   <bean class="org.apache.ignite.configuration.CacheConfiguration">
    ...
    <!-- Cache store. -->
    <property name="cacheStoreFactory">
     <bean class="org.apache.ignite.cache.store.jdbc.CacheJdbcPojoStoreFactory">
       <property name="dataSourceBean" value = "sampleDataSource"/>
       <property name="types">  
         <list>
           ... Copy here types generated by wizard...
         </list>  
       </property>
     </bean>
    </property>
    
    <!-- Enable store usage. --> 
    <!-- Sets flag indicating whether read from database is enabled. -->
    <property name="readThrough" value="true"/>
     
    <!-- Sets flag indicating whether write to database is enabled. -->
    <property name="writeThrough" value="true"/>
     
    <!-- Enable database batching. --> 
    <!-- Sets flag indicating whether write-behind is enabled. -->
    <property name="writeBehindEnabled" value="true"/>
  </list>
 </property>
...
</bean>
IgniteConfiguration cfg = new IgniteConfiguration();
...

// Configure store factory. 
CacheJdbcPojoStoreFactory storeFactory = new CacheJdbcPojoStoreFactory();
storeFactory.setDataSource(new H2DataSourceFactory());

// Configure cache to use cache store. 
CacheConfig.cache("Sample", storeFactory);
  
cfg.setCacheConfiguration(ccfg);
  
...
// Start Ignite node.
Ignition.start(cfg);
/**
 * Datasource to use for store tests.
 */
public class H2DataSourceFactory implements Factory<DataSource> {
    /** DB connection URL. */
    private static final String DFLT_CONN_URL = "jdbc:h2:tcp://localhost/mem:ExampleDb";

    /** {@inheritDoc} */
    @Override public DataSource create() {
        return JdbcConnectionPool.create(DFLT_CONN_URL, "sa", "");
    }
}

All operations defined in Persistent Store are available in CacheJdbcPojoStore.

CacheJdbcPojoStore can effectively load data. To load all data from database for all types registered in cache configuration - just call IgniteCache.loadCache(null) method. To load data with custom conditions you should pass key types and SQL queries to IgniteCache.loadCache() method. For example:

Ignite ignite = Ignition.ignite();

IgniteCache<Long, Person> c = ignite.getOrCreateCache("myCache");
c.loadCache(null, "java.lang.Integer", "select * from Person where id > 100");

Example

Example org.apache.ignite.examples.datagrid.store.auto.CacheAutoStoreExample demonstrates usage of cache store.

In order to run example for automatic persistence you need:

  • Start H2 server using DbH2ServerStartup.
  • Start several nodes using ExampleNodeStartup.
  • Start CacheAutoStoreExample.

Demo

Lets do step-by-step demo. You can find demo sources in examples/schema-import folder.

We will use H2 database.

  • Start H2 server with following script: examples/schema-import/bin/h2-server.{sh|bat}

H2 Console will be started in your default browser.

  • Connect to H2 with following settings:

    • Select Generic H2 (Server) settings.
    • Specify JDBC URL as jdbc:h2:tcp://localhost/~/schema-import/demo.
  • Click Connect.

  • Paste content of examples/schema-import/bin/db-init.sql into H2 Console and execute.

  • Start Schema Import Wizard with properties configured for demo:

 bin/ignite-schema-import.sh examples/schema-import/bin/schema-import.properties
  • Click Next.
  • Click Generate. Answer Yes for override warnings.

POJO files and java cache cinfiguration will be generated in examples/schema-import/src/main/java/org/apache/ignite/schema folder.

  • Import in your favorite IDE examples/schema-import/pom.xml.

  • Run Demo.java.

Automatic Persistence

Automatically read-through and write-through your domain model to and from a database.