Skip to main content

Apache Ignite with JPA : A missing element


The Apache Ignite bookA new version of using Hibernate OGM described in the The Apache Ignite book. If it got you interested, check out the rest of the book for more helpful information.

Often the first step to developing an Enterprise information system is creating the domain model, that is, listing the entities in the domain and defining the relationships between them. A domain model is a conceptual image of the problem your system is trying to solve. Domain model elements can be linked by relationships. Usually, relational objects are represented in a tabular format, while application object model are represented in an interconnected graph of the object format. While storing and retrieving an object model from the relational database, a few mismatch occurs such as Granularity, SubTypes etc. To solve the mismatches between relational and object model, JPA provides a collection of APIs and methods to manipulates with the persistence store. The JPA specification only defines relational database access, but its API and many of its annotations are not relational specific. There are a few factors we have to take into account before applying JPA into any NoSQL database:

1. Pure relational concepts may not apply well in NoSQL
  • a. Table, Column, Joins.
2. JPA queries may not be suitable for NoSQL. NoSQL data modeling is typically driven by application-specific access patterns.

Note that, if your dataset is by nature non-domain model centric, then JPA is not for you.

Anyway, Apache Ignite provides in-memory KeyValue store and it’s quite well fit for using JPA. Other NoSQL vendor like Infinspan, Oracle NoSQL, Ehcache also supported by JPA persistence as well. There are a few NoSQL/JPA solutions available in today's market.
• Kundera
  • o One of the very first JPA implementations for NoSQL databases.
  • o Supports Cassandra, MongoDB, Hbase, Redis, Oracle NoSQL DB etc.
• DataNucleus
  • o Persistence layer behind Google App engine
  • o Supports MongoDB, Cassandra, Neo4J
• Hibernate OGM
  • o Using Hibernate ORM engine to persists entities in NoSQL database.
  • o Supports MongoDB, Cassandra, Neo4j, Infinspan, Ehcache
  • o Experipental support for Apache Ignite.
Hibernate OGM talks to NoSQL database via store-specific dialects. Hibernate OGM or Hibernate Object Grid Mapper also supports several ways for searching entities and returning them as Hibernate managed objects:

1. JP-QL queries (we convert them into a native backend query)
2. datastore specific native queries
3. full-text queries, using Hibernate Search as indexing engine

So, for Apache Ignite we are going to give a try to use JPA by Hibernate OGM framework. Note that, Apache Ignite support by Hibernate OGM still in development stage and not recommended to use in production. The project is available at Github repositories and any contributions are welcome. Anyway, you can also contribute to code review of this project with others by this URL. High-level view of the Hibernate OGM are shown below:

In the next few section we will cover the following topics:

• Clone and build the module Ignite for Hibernate OGM framework.
• Create a new maven project for using Hibernate OGM with Ignite.
• Persisting a few entities in Ignite through JPA.

Before we start, make sure the prerequisites of the project in your workstation:
1. Java JDK 1.8
2. Ignite version 1.7.0
3. Apache Maven version >3.0.3

Step 1:
Let’s set up our sandbox first. Clone or download the Hibernate OGM framework source code from the GitHub repositories.

git clone git@github.com:Z-z-z-z/hibernate-ogm.git hibernate-ogm

Step 2:

Modify the pom.xml, comment the following modules as follows:
<module>infinispan</module> 
<module>infinispan-remote</module> 
<module>mongodb</module>
<module>neo4j</module>
<module>couchdb</module>
<module>cassandra</module>
<module>redis</module>
We donot need these above modules in our project. Make sure that, you have the ignite module on pom.xml file.

Step 3:
Build the project with the following command:
mvn clean install -Dmaven.test.skip=true -DskipDocs -DskipDistro
If everything goes well, you should have all the necessary libraries in your local maven repositories.

Step 4:
Clone or download the ignite-jpa repository from the GitHub. If you create your own maven project, add this dependencies of your pom.xml.
<dependency>
    <groupId>org.hibernate.ogm</groupId>
    <artifactId>hibernate-ogm-ignite</artifactId>
    <version>5.1.0-SNAPSHOT</version>
</dependency>
<dependency>
    <groupId>org.hibernate.javax.persistence</groupId>
    <artifactId>hibernate-jpa-2.1-api</artifactId>
    <version>1.0.0.Final</version>
</dependency>

The dependencies are:
1. The hibernate OGM Ignite module for working with Apache Ignite cache. This will pull in all other required modules such as Hibernate OGM core.
2. Hibernate JPA API to working with JPA.

The domain model:
Our example domain model is consisted of two entities: Breed and Dog.

The association between Breed and Dog is a ManyToOne. One Dog can have only one breed and so on.
Step 5:
Now let’s map the domain model by creating the entity Java classes and annotating them with the required meta information. Let’s strat with the Breed class.

@Entity(name = "BREED")
public class Breed {
    @Id
    @GeneratedValue(generator = "uuid")
    @GenericGenerator(name="uuid", strategy="uuid2")
    private String id;

    private String name;

    public String getId() { return id; }
    public void setId(String id) { this.id = id; }

    public String getName() { return name; }
    public void setName(String name) { this.name = name; }

    @Override
    public String toString() {
        return "Breed{" +
                "id='" + id + '\'' +
                ", name='" + name + '\'' +
                '}';
    }
}

The entity is marked as a JPA annotation of @Entity, while other properties such as ID are annoted by the @ID.
By the @ID annotation, Hibernate will take care to generate the primary key or the key value for the entity object. @GeneratedValue UUID will generate a UUID value as a entity identifier.
Create another class named Dog and add the following contents on it.

@Entity
public class Dog {
    @Id
    @GeneratedValue(strategy = GenerationType.TABLE, generator = "dog")
    public Long getId() { return id; }
    public void setId(Long id) { this.id = id; }
    private Long id;

    public String getName() { return name; }
    public void setName(String name) { this.name = name; }
    private String name;

    @ManyToOne
    public Breed getBreed() { return breed; }
    public void setBreed(Breed breed) { this.breed = breed; }
    private Breed breed;

    @Override
    public String toString() {
        return "Dog{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", breed=" + breed +
                '}';
    }
}

We also annotated the Dog entity with @Entity and @ID annotation. Also we add one @ManyToOne annotation to make the association with Breed entity.

Step 6:
Let’s create the cache configuration class and the persistence.xml. Create a Ignite cache configuration class with name ConfigurationMaker as follows:
public class ConfigurationMaker implements IgniteConfigurationBuilder {
    @Override
    public IgniteConfiguration build() {
        IgniteConfiguration config = new IgniteConfiguration();
        config.setPeerClassLoadingEnabled(true);
        config.setClientMode(false);
        TcpDiscoverySpi discoSpi = new TcpDiscoverySpi();
        TcpDiscoveryMulticastIpFinder ipFinder = new TcpDiscoveryMulticastIpFinder();
        ArrayList addrs = new ArrayList<>();
        addrs.add("127.0.0.1:47500..47509");
        ipFinder.setAddresses(addrs);
        discoSpi.setIpFinder(ipFinder);
        config.setDiscoverySpi(discoSpi);

        CacheConfiguration accountCacheCfg = new CacheConfiguration()
                .setName("BREED")
                .setAtomicityMode(TRANSACTIONAL)
                .setIndexedTypes(
                        String.class, Breed.class
                );

        config.setCacheConfiguration(accountCacheCfg);
        return config;
    }
}

The above class represented the Ignite Cache configuration, instead of using spring configuration. We have explained the cache configurarion in chapter one. Let’s create the persistence.xml file in /ignite-jpa/src/main/resources/META-INF/persistence.xml directory.

<?xml version="1.0"?>
<persistence xmlns="http://java.sun.com/xml/ns/persistence"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd"
             version="2.0">

    <persistence-unit name="ogm-jpa-tutorial" transaction-type="RESOURCE_LOCAL">
        <provider>org.hibernate.ogm.jpa.HibernateOgmPersistence</provider>
        <properties>

            <property name="com.arjuna.ats.jta.jtaTMImplementation" value="com.arjuna.ats.internal.jta.transaction.arjunacore.TransactionManagerImple"/>
            <property name="com.arjuna.ats.jta.jtaUTImplementation" value="com.arjuna.ats.internal.jta.transaction.arjunacore.UserTransactionImple"/>

            <property name="hibernate.ogm.datastore.provider" value="IGNITE_EXPERIMENTAL"/>
            <property name="hibernate.ogm.ignite.configuration_class_name" value="com.blu.imdg.exampleOgm.ConfigurationMaker"/>
        </properties>
    </persistence-unit>
</persistence>

If you have familiar with JPA before, this persistence definition unit should look very common to you. The main difference to using the classic Hibernate ORM on top of a relational database is the specific provider class we need to specify for Hibernate OGM: org.hibernate.ogm.jpa.HibernateOgmPersistence. Also Note that, we are using RESOURCE_LOCAL instead of JTA. If you want to use JTA, you should have provided a particular JTA implementation such as JBOSS. In addition, we have also specified the following configurations:

• DataStore provide: IGNITE_EXPERIMENTAL
• Configuration_class_name : Ignite configuration (ConfigurationMaker)

Step 7:
Let’s now persist a set of entities and retrieve them. Create a class with name TestOgm with following the following content:
public class TestOgm {
    public static void main(String[] args) throws SystemException, NotSupportedException, HeuristicRollbackException, HeuristicMixedException, RollbackException {
        EntityManagerFactory emf = Persistence.createEntityManagerFactory("ogm-jpa-tutorial");

        EntityManager em =  emf.createEntityManager();
        em.getTransaction().begin();

        Breed collie = new Breed();
        collie.setName("breed-collie");
        em.persist(collie);

        Dog dina = new Dog();
        dina.setName("dina");
        dina.setBreed(collie);
        //persis dina
        em.persist(dina);
        em.getTransaction().commit();
        //get ID dina
        Long dinaId = dina.getId();
        // query
        Dog ourDina =  em.find(Dog.class, dinaId);
        System.out.println("Dina:" + ourDina);

        em.close();

    }

    private static TransactionManager extractJBossTransactionManager(EntityManagerFactory factory) {
        SessionFactoryImplementor sessionFactory = (SessionFactoryImplementor) ( (HibernateEntityManagerFactory) factory ).getSessionFactory();
        return sessionFactory.getServiceRegistry().getService( JtaPlatform.class ).retrieveTransactionManager();
    }
}
First we have created a EntityManagerFactory with parameter “ogm-jpa-tutorial”. Next, we derived our EntityManager from the factory, this EntityManager will be our entry point for persistence entities. We opened a transaction from the EntityManager and create our Breed with name breed-collie. Persist the breed-collie with the entityManager persist() method. Also created an another instance of Dog: dina and associated with it breed-collie. Next we persist the dog dina in cache with the same method persist() and retrieve the instance by the find() method.
Step 8:
Let’s build and run the application. Before run the class TestOgm, we have to run an instance of the Ignite node. Run the following command to start an instance of Ignite node.

mvn exec:java -Dexec.mainClass=com.blu.imdg.StartCacheNode
Now run the following command to execute the TestOgm class as follows:
mvn exec:java -Dexec.mainClass=com.blu.imdg. exampleOgm.TestOgm
You should notice a lot of log into console, with the following entries:
Two entries have been flushed into the Ignite cache and retrieve the dog Dina from the cache. Let’s explorer the cache through Ignite Visor.
Two different cache has been created for the entities: Breed and Dog. If we scan the cache entries of the Dog cache, we should find the following entity on it.

Entity Dina has been persisted into the cache with the key of the Breed collie. Unfortunately, Hibernate HQL or Search is not working in this experimental version of this Hibernate OGM Ignite module. All the hibernate features are under development and will be supported soon.

Comments

Popular posts from this blog

Send e-mail with attachment through OSB

Oracle Service Bus (OSB) contains a good collection of adapter to integrate with any legacy application, including ftp, email, MQ, tuxedo. However e-mail still recognize as a stable protocol to integrate with any application asynchronously. Send e-mail with attachment is a common task of any business process. Inbound e-mail adapter which, integrated with OSB support attachment but outbound adapter doesn't. This post is all about sending attachment though JavaCallout action. There are two ways to handle attachment in OSB: 1) Use JavaCallout action to pass the binary data for further manipulation. It means write down a small java library which will get the attachment and send the e-mail. 2) Use integrated outbound e-mail adapter to send attachment, here you have to add a custom variable named attachment and assign the binary data to the body of the attachment variable. First option is very common and easy to implement through javax.mail api, however a much more developer manage t

Tip: SQL client for Apache Ignite cache

A new SQL client configuration described in  The Apache Ignite book . If it got you interested, check out the rest of the book for more helpful information. Apache Ignite provides SQL queries execution on the caches, SQL syntax is an ANSI-99 compliant. Therefore, you can execute SQL queries against any caches from any SQL client which supports JDBC thin client. This section is for those, who feels comfortable with SQL rather than execute a bunch of code to retrieve data from the cache. Apache Ignite out of the box shipped with JDBC driver that allows you to connect to Ignite caches and retrieve distributed data from the cache using standard SQL queries. Rest of the section of this chapter will describe how to connect SQL IDE (Integrated Development Environment) to Ignite cache and executes some SQL queries to play with the data. SQL IDE or SQL editor can simplify the development process and allow you to get productive much quicker. Most database vendors have their own front-en

Load balancing and fail over with scheduler

Every programmer at least develop one Scheduler or Job in their life time of programming. Nowadays writing or developing scheduler to get you job done is very simple, but when you are thinking about high availability or load balancing your scheduler or job it getting some tricky. Even more when you have a few instance of your scheduler but only one can be run at a time also need some tricks to done. A long time ago i used some data base table lock to achieved such a functionality as leader election. Around 2010 when Zookeeper comes into play, i always preferred to use Zookeeper to bring high availability and scalability. For using Zookeeper you have to need Zookeeper cluster with minimum 3 nodes and maintain the cluster. Our new customer denied to use such a open source product in their environment and i was definitely need to find something alternative. Definitely Quartz was the next choose. Quartz makes developing scheduler easy and simple. Quartz clustering feature brings the HA and