How to: CRUD and JPA association handling using Kundera

by Vivek Mishra


Recently we have released Kundera-2.0.5. This post is all about demonstrating how to perform CRUD and association handling using kundera. Kundera is now enabled to use secondary index support provided by cassandra(0.7.x onwards), Hence this example will demonstrate how to leverage that benefit using same JPA style within in Kundera
Example i am referring can be found Here.
Run this script to create column family in cassandra with indexes: =>
  • create keyspace KunderaExamples;
  • create column family PERSON with comparator=UTF8Type and column_metadata=[{column_name: PERSON_NAME, validation_class:UTF8Type, index_type: KEYS}, {column_name: AGE, validation_class:IntegerType, index_type: KEYS}];

Entity: PersonCassandra.java

package com.impetus.kundera.examples.crud;import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Table;/**
* The Class Person.
*/
@Entity
@Table(name = "PERSON", schema = "KunderaExamples@twissandra")
public class PersonCassandra
{/** The person id. */
@Id
@Column(name = "PERSON_ID")
private String personId;/** The person name. */
@Column(name = "PERSON_NAME")
private String personName;/** The age. */
@Column(name = "AGE")
private Integer age;// Followed by getters and setters method
}


Configuration : Persistence.xml

<persistence xmlns=”http://java.sun.com/xml/ns/persistence&#8221;
xmlns:xsi=”http://www.w3.org/2001/XMLSchema-instance&#8221;
xsi:schemaLocation=”http://java.sun.com/xml/ns/persistence
https://raw.github.com/impetus-opensource/Kundera/Kundera-2.0.4/kundera-core/src/test/resources/META-INF/persistence_2_0.xsd&#8221;
version=”2.0″>

<!– Persistence Units for twissandra application –>
<persistence-unit name=”twissandra”>
<provider>com.impetus.kundera.KunderaPersistence</provider>
<properties>
<property name=”kundera.nodes” value=”localhost”/>
<property name=”kundera.port” value=”9160″/>
<property name=”kundera.keyspace” value=”KunderaExamples”/>
<property name=”kundera.dialect” value=”cassandra”/>
<property name=”kundera.client” value=”Pelops”/>
<property name=”kundera.cache.provider.class” value=”com.impetus.kundera.cache.ehcache.EhCacheProvider”/>
<property name=”kundera.cache.config.resource” value=”/ehcache-test.xml”/>
</properties>
</persistence-unit>
</persistence>

Now, if you notice @ table annotation:


@Table(name = “PERSON”, schema = “KunderaExamples@twissandra”)

For cassandra, “PERSON” is specified column family and schema denotes “keyspace@puname”.

Entity definition:
public class PersonTest
{
/** The emf. */
private EntityManagerFactory emf;
/** The em. */
private EntityManager em;

.... methods defining various operations.

}

Initialize entity manager
emf = Persistence.createEntityManagerFactory("twissandra");
em = emf.createEntityManager();
Insert :
public void onInsertCassandra()
{Object p1 = prepareData("1", 10);
Object p2 = prepareData("2", 20);
Object p3 = prepareData("3", 15);
em.persist(p1);
em.persist(p2);
em.persist(p3);
}

private PersonCassandra prepareData(String rowKey, int age)
{
PersonCassandra o = new PersonCassandra();
o.setPersonId(rowKey);
o.setPersonName("vivek");
o.setAge(age);
return o;
}

Find By Id:
public void onFindByIdCassandra()
{
PersonCassandra p = findById(PersonCassandra.class, "1", em);
}
private E findById(Classclazz, Object rowKey, EntityManager em)
{
return em.find(clazz, rowKey);
}
Find By Name:
public void onFindByName()
{
findByName(em, "PersonCassandra", PersonCassandra.class, "vivek", "PERSON_NAME");
}
/**
* Assert find by name.
*
* @param the element type
* @param em the em
* @param clazz the clazz
* @param e the e
* @param name the name
* @param fieldName the field name
*/
private void findByName(EntityManager em, String clazz, E e, String name, String fieldName)
{

String query = "Select p from " + clazz + " p where p."+fieldName+" = "+name;
// // find by name.
Query q = em.createQuery(query);
List results = q.getResultList();
}

Find By Name and Age:

public void onFindByNameAndAge()
{
findByNameAndAge(em, "PersonCassandra", PersonCassandra.class, "vivek", "10", "PERSON_NAME");
}
private void findByNameAndAge(EntityManager em, String clazz, E e, String name, String minVal, String fieldName)
{

Query q = em.createQuery("Select p from " + clazz + " p where p."+fieldName+" = "+name+" and p.AGE > "+ minVal);
List results = q.getResultList();

}

Find By Range:
public void onFindByRange()
{
findByRange(em, "PersonCassandra", PersonCassandra.class, "10", "20", "PERSON_ID");
}
private void findByRange(EntityManager em, String clazz, E e, String minVal, String maxVal, String fieldName)

{
// find by Range.
Query q = em.createQuery("Select p from " + clazz + " p where p."+fieldName+" Between "+minVal+" and "+maxVal);
List results = q.getResultList();
}

Find by Name and “<” and “>”

public void onFindByNameAndAgeGTAndLT()
{
findByNameAndAgeGTAndLT(em, "PersonCassandra", PersonCassandra.class, "vivek", "10", "20", "PERSON_NAME");
}
private void findByNameAndAgeGTAndLT(EntityManager em, String clazz, E e, String name, String minVal, String maxVal, String fieldName)
{
// // // find by name, age clause
Query q = em.createQuery("Select p from " + clazz
+ " p where p."+fieldName+" = " + name + " and p.AGE > "+ minVal+ " and p.AGE < " +maxVal);
List results = q.getResultList();

}

Self Association:

Example demonstrating about how to define and perform bi directional self association is available Here

Using Default lucene index

Still there are some operations not supported by enabling cassandra secondary indexes(e.g. indexing and search over super column values etc.). Also indexing support over HBase is not yet mature, so Kundera does provide default lucene indexing support for all sort of find operation. What you need to do is simply provide given below property:

<property name=”index_home_dir” value=”$LUCENE_DIR_PATH”/>

This will simply start storing and indexing records on specified local/remote location.

Can we use same example for other supported data stores(e.g. Mongo, HBase, Mysql etc) ?

Answer is YES.  changes required:

  • Define persistence unit in persistence.xml
  • Create script specific to intended database.
  • Modify entity definition (e.g. PersonCassandra) for correct column family name or table name.(see @table annotation above)
  • Modify entity manager factory instantiation for correct column family name.

That’s it !

References:
  • Example to refer for different data type support can be found Here
  • Example to refer for cross data store operations can be found Here
  • Example to refer for flickr like application can be found here