Over a million developers have joined DZone.
{{announcement.body}}
{{announcement.title}}

Apache Ignite With JPA: A Missing Element

DZone's Guide to

Apache Ignite With JPA: A Missing Element

Learn how to persist your entities with Apache Ignite and JPA. This tutorial will guide you through the setup of execution of that handy ability.

· Database Zone
Free Resource

Traditional relational databases weren’t designed for today’s customers. Learn about the world’s first NoSQL Engagement Database purpose-built for the new era of customer experience.

Portions of this article were taken from the persistence chapter of the book, High-Performance In-Memory Computing With Apache Ignite. 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 models are represented in an interconnected graph of the object format. While storing and retrieving an object model from the relational database, a few mismatches occur, such as Granularity, SubTypes, etc. To solve the mismatches between the relational and object models, JPA provides a collection of APIs and methods to manipulate 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 to any NoSQL database:

  1. Pure relational concepts may not apply well in NoSQL, like tables, columns, and 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 an in-memory KeyValue store, and it’s quite a good fit for using JPA. Other NoSQL vendors, like Infinspan, Oracle NoSQL, and Ehcache are also supported by JPA persistence as well. There are a few NoSQL/JPA solutions available in today's market.

1. Kundera

  • One of the very first JPA implementations for NoSQL databases.
  • Supports Cassandra, MongoDB, HBase, Redis, Oracle NoSQL DB, etc.

2. DataNucleus

  • Persistence layer behind the Google App engine.
  • Supports MongoDB, Cassandra, and Neo4J.

3. Hibernate OGM

  • Using Hibernate ORM engine to persists entities in NoSQL database.
  • Supports MongoDB, Cassandra, Neo4j, Infinspan, Ehcache
  • Experimental support for Apache Ignite.

Hibernate OGM talks to NoSQL databases via store-specific dialects. Hibernate OGM, or Hibernate Object Grid Mapper, also supports several ways of searching entities and returning them as Hibernate-managed objects:

1. JP-QL queries.

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 is still in the development stage and not recommended to use in production. The project is available on Github, and any contributions are welcome. Anyway, you can also contribute to the code review of this project with this URL. A high-level view of the Hibernate OGM is shown below:

Hibernate OGM

In the next few sections, we will cover the following topics:

  • Clone and build the Ignite module for the Hibernate OGM framework.

  • Create a new Maven project for using Hibernate OGM with Ignite.

  • Persisting a few entities into Ignite caches through JPA.

Before we start, let's cover the prerequisites of the project in your sandbox:

  1. Java JDK 1.8

  2. Ignite version 1.7.0

  3. Apache Maven version >3.0.3

Step 1

Let’s set up the 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, delete the following modules:

<module>infinispan</module> 
<module>infinispan-remote</module> 
<module>mongodb</module>
<module>neo4j</module>
<module>couchdb</module>
<module>cassandra</module>
<module>redis</module>


We don't 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 these dependencies to 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 the Apache Ignite cache. This Maven dependency will pull all other required modules, such as Hibernate OGM core and so on.
  2. Hibernate JPA API to work with JPA.

The Domain Model

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

Domain Model

The association between Breed and Dog is ManyToOne. One Dog can have only one breed.

Step 5

Now, let’s map the domain model by creating the Java classes and annotating them with the required meta-information. Let’s start with the Breed class.

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

    private String name;

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

    public String getName() { returnname; }
    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 annotated 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 an entity identifier.

Create another class named Dog and add the following contents to it.

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

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

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

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


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

Step 6

Let’s create the cache configuration class and the persistence.xml. Create an Ignite cache configuration class with the name ConfigurationMaker as follows:

public class ConfigurationMaker implements IgniteConfigurationBuilder {
    @Override
    public IgniteConfiguration build() {
        IgniteConfiguration config = newIgniteConfiguration();
        config.setPeerClassLoadingEnabled(true);
        config.setClientMode(false);
        TcpDiscoverySpi discoSpi = newTcpDiscoverySpi();
        TcpDiscoveryMulticastIpFinder ipFinder = newTcpDiscoveryMulticastIpFinder();
        ArrayList<string> addrs = newArrayList<>();
        addrs.add("127.0.0.1:47500..47509");
        ipFinder.setAddresses(addrs);
        discoSpi.setIpFinder(ipFinder);
        config.setDiscoverySpi(discoSpi);

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

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


The above class represents the Ignite Cache configuration. The cache configuration is explained here. Let’s create the persistence.xml file in the /ignite-jpa/src/main/resources/META-INF/persistence.xml directory.

<?xmlversion="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/persistencehttp://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>
            <propertyname="com.arjuna.ats.jta.jtaTMImplementation"value="com.arjuna.ats.internal.jta.transaction.arjunacore.TransactionManagerImple"/>
            <propertyname="com.arjuna.ats.jta.jtaUTImplementation"value="com.arjuna.ats.internal.jta.transaction.arjunacore.UserTransactionImple"/>
            <propertyname="hibernate.ogm.datastore.provider"value="IGNITE_EXPERIMENTAL"/>
            <propertyname="hibernate.ogm.ignite.configuration_class_name"value="com.blu.imdg.exampleOgm.ConfigurationMaker"/>
        </properties>
    </persistence-unit>
</persistence>


If you are familiar with JPA, 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 provide 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 persist a set of entities and retrieve them from the Ignite caches. Create a class with the name TestOgm and add 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 = newBreed();
        collie.setName("breed-collie");
        em.persist(collie);

        Dog dina = newDog();
        dina.setName("dina");
        dina.setBreed(collie);
        //persist 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();
        returnsessionFactory.getServiceRegistry().getService( JtaPlatform.class).retrieveTransactionManager();
    }
}


First, we created an EntityManagerFactory with the 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 created an instance of the Breed named breed-collie. We then persisted the breed-collie instance with the entityManager's persist() method. We also created an another instance of Dog, dina, and associated it with the breed-collie instance. Next, we persisted the dog instance dina in the cache with the same method persist() and retrieved the instance with the find() method.

Step 8

Let’s build and run the application. Before we run the class TestOgm, we have to run an instance of the Ignite node. Run the following command to start an instance of the 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 find a lot of log messages into the console:

Persist log

The log messages confirm that two entries (dina and breed-collie) have been flushed into the Ignite cache and retrieved the dog Dina from the cache. Let’s explore the cache through Ignite Visor.

Caches by entity

Two different caches have 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.

Cache entries

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

Learn how the world’s first NoSQL Engagement Database delivers unparalleled performance at any scale for customer experience innovation that never ends.

Topics:
apache ignite ,jpa ,persistence ,database

Published at DZone with permission of Shamim Bhuiyan. See the original article here.

Opinions expressed by DZone contributors are their own.

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}