The Java Persistence API (JPA) is the Java API for persistence management and object/relational mapping in a Java EE/Java SE environment with which a Java domain model is used to manage a relational database. JPA also provides a query language API with the Query
interface for static and dynamic queries. JPA is designed primarily for relational databases.
Kundera is a JPA 2.0–compliant object–data store mapping library for NoSQL data stores. Kundera also supports relational databases and provides NoSQL data store–specific configuration for Apache Cassandra and some other NoSQL databases, including HBase and MongoDB. Using the Kundera library in the domain model, a NoSQL database can be accessed using the JPA. In this chapter, you will access Apache Cassandra with Kundera and run CRUD operations on Cassandra.
To set the environment, you must install the following software:
The Kundera library for Apache Cassandra kundera-cassandra-2.2.1-jar-with-dependencies.jar from https://github.com/impetus-opensource/Kundera/downloads.
A persistence framework including support for Java Persistence (JPA) 2.0 JSR 317-EclipseLink 2.4.2 from http://www.eclipse.org/eclipselink/downloads/index.php#242. Extract the eclipselink-2.4.2.v20130514-5956486.zip file to a directory.
An implementation of JPA 2.0 eclipselink-2.4.2.jar from http://repo1.maven.org/maven2/org/eclipse/persistence/eclipselink/2.4.2/eclipselink-2.4.2.jar.
Apache Cassandra 2.04 or a later version from http://cassandra.apache.org/download/.
Later versions than those listed may also be used.
You need to create a keyspace for object/relational mapping using Kundera. In Cassandra-Cli, run the following command to create a keyspace called Kundera
using a replica placement strategy org.apache.cassandra.locator.SimpleStrategy
and a replication_factor
of 1
.
CREATE KEYSPACE Kundera with placement_strategy = 'org.apache.cassandra.locator.SimpleStrategy' and strategy_options = {replication_factor:1};
Next, run the following command in Cassandra-Cli to use the Kundera
keyspace:
use Kundera;
The output from the Cassandra-Cli commands is shown in Figure 9.1.
You also need to create a column family for object/relational persistence. Run the following command in Cassandra-Cli to create a column family called catalog
:
CREATE COLUMN FAMILY catalog WITH comparator = UTF8Type AND key_validation_class=UTF8Type AND column_metadata = [ {column_name: catalogId, validation_class: UTF8Type, index_type: KEYS}, {column_name: journal, validation_class: UTF8Type}, {column_name: publisher, validation_class: UTF8Type}, {column_name: edition, validation_class: UTF8Type}, {column_name: title, validation_class: UTF8Type, index_type: KEYS}, {column_name: author, validation_class: UTF8Type} ];
The output from the command is shown in Figure 9.2.
In this section, you will create a JPA project in the Eclipse IDE for the Kundera Cassandra application. Follow these steps:
1. Select File > New > Other.
2. In the New dialog box, select JPA > JPA Project. Then click Next, as shown in Figure 9.3.
3. In the New JPA Project wizard, specify a project name (Kundera), choose a project location, select JDK 1.7 as the target runtime, and 2.0 as the JPA version. In the Configuration drop-down list, select Default Configuration for jdk1.7.0_21 and click Next, as shown in Figure 9.4.
4. In the Java Settings dialog box, choose src
in the Source Folders on Build Path box and set the default output folder to buildclasses. These are also the default Java settings. Then click Next, as shown in Figure 9.5.
5. Configure a JPA facet. In the Platform drop-down list, choose EclipseLink 2.4.x. In the Type drop-down list under JPA Implementation, choose User Library. Then click the Manage Libraries button to create a new user library, as shown in Figure 9.6.
6. Choose Preferences > User Libraries. Then click New to create a new user library for EclipseLink 2.4. In New User Library dialog box, specify a user library name (EclipseLink2.4) and click OK, as shown in Figure 9.7.
7. The EclipseLink2.4 user library is created. Click the Add External JARs button, shown in Figure 9.8, to add the javax.persistence_2.0.5.v201212031355.jar file from the jpa subfolder of the \eclipselink-2.4.2.v20130514-5956486eclipselinkjlib directory.
8. Add the eclipselink-2.4.2.jar file to the EclipseLink2.4 user library and click OK. The EclipseLink2.4 user library is added to new JPA project, as shown in Figure 9.9. Click Finish.
9. An Open Associated Perspective dialog box prompts you to open the JPA perspective. Click Yes, as shown in Figure 9.10. The EclipseLink2.4 library is added to the Java build path of the Kundera JPA project, as shown in the Properties for Kundera dialog box.
10. Click the Add External JARs button to add the kundera-cassandra-2.2.1-jar-with-dependencies.jar file to the Java build path with the Add External JARs button. The JAR files listed in Table 9.1 are included in the Kundera project’s Java build path. The libraries and JARs in the Java build path of the Kundera project are shown in Figure 9.11.
The Kundera JPA project is created. The JPA project includes a META-INF/persistence.xml file for configuring properties for the object/relational mapping, as shown in Figure 9.12.
The domain model for a JPA object/relational mapping application is defined in a JPA entity class. The domain model class is just a plain old Java object (POJO) that describes the Java object entity to be persisted, the object properties, and the Cassandra keyspace and column family to persist to.
In this section, you will create a JPA entity class for object/relational mapping using Kundera and the Cassandra database. Cassandra, though not a relational database, can be used with object/relational mapping using the Kundera library, which supports mainly NoSQL databases. Follow these steps:
1. Choose File > New > Other.
2. In the New dialog box, choose JPA > JPA Entity. Then click Next, as shown in Figure 9.13.
3. In the New JPA Entity wizard, select the Kundera project (a JPA project is required for a JPA entity), select a source folder (kundera/src), specify a Java package (kundera
), and specify a class name (Catalog
). In the Inheritance section, choose the Entity option button. Then click Next, as shown in Figure 9.14.
4. In the Entity Properties dialog box, select the Use Default checkbox to select the default table name, Catalog
. Then, under Access Type, leave the default setting, Field, selected. Finally, click Finish, as shown in Figure 9.15. The Catalog JPA entity is created.
Annotate the Catalog
class with an @Entity
annotation to indicate that the class is a JPA entity class. By default, the entity name is the same as the entity class name. Annotate the class with @Table
to indicate the Cassandra table name and schema. The table name is the column family name catalog
. The schema is in Keyspace@persistence-unit
format. For the Kundera
keyspace and the kundera
persistence unit name, which you will configure in the next section, the schema is Kundera@kundera
.
@Entity(name = "catalog") @Table(name = "catalog", schema = "Kundera@kundera")
The entity class implements the Serializable
interface to serialize a cache-enabled entity bean to a cache when persisted to a database. To associate a version number with a serializable class by serialization runtime, specify a serialVersionUID
variable.
private static final long serialVersionUID = 1L;
Annotate the catalogId
field with the @Id
annotation to indicate that the field is the primary key of the entity.
@Id private String catalogId;
The primary key column name in the Cassandra database is assumed to be the name of the primary key of the entity class. The field annotated with @Id
must be one of the following types:
Java primitive type, such as int
or double
Any primitive wrapper type, such as Integer, Double
, String
, java.util.Date
, java.sql.Date
, java.math.BigDecimal
, or java.math.BigInteger
Add fields called journal
, publisher
, edition
, title
, and author
, and annotate them with the @Column
annotation to indicate that the fields are mapped to columns in the Cassandra table. (Recall that in Cassandra, a column family is also called a table.)
@Column(name = "journal") private String journal; @Column(name = "publisher") private String publisher; @Column(name = "edition") private String edition; @Column(name = "title") private String title; @Column(name = "author") private String author;
Add get
/set
methods for each of the fields. The JPA entity class Catalog
appears in Listing 9.1.
Listing 9.1 The JPA Entity Class
package kundera; import java.io.Serializable; import javax.persistence.*; /** * Entity implementation class for Entity: Catalog * */ @Entity(name = "catalog") @Table(name = "catalog", schema = "Kundera@kundera") public class Catalog implements Serializable { private static final long serialVersionUID = 1L; @Id private String catalogId; public Catalog() { super(); } @Column(name = "journal") private String journal; @Column(name = "publisher") private String publisher; @Column(name = "edition") private String edition; @Column(name = "title") private String title; @Column(name = "author") private String author; public String getCatalogId() { return catalogId; } public void setCatalogId(String catalogId) { this.catalogId = catalogId; } public String getJournal() { return journal; } public void setJournal(String journal) { this.journal = journal; } public String getPublisher() { return publisher; } public void setPublisher(String publisher) { this.publisher = publisher; } public String getEdition() { return edition; } public void setEdition(String edition) { this.edition = edition; } public String getTitle() { return title; } public void setTitle(String title) { this.title = title; } public String getAuthor() { return author; } public void setAuthor(String author) { this.author = author; } }
A META-INF/persistence.xml configuration file was created when a JPA project was created in the Eclipse IDE. In this section, you will configure the object/relational mapping in the persistence.xml configuration file. Kundera supports some properties, specified in persistence.xml with the <property/>
tag, common to all NoSQL data stores it supports. These common properties are discussed in Table 9.2.
In the persistence.xml file for the Kundera project, specify the persistence-unit
name as "kundera"
. Add a <provider/>
element set to com.impetus.kundera.KunderaPersistence.
Specify the JPA entity class as kundera.Catalog
in the <class/>
element. Add <property/>
tags grouped as sub-elements of the <properties/>
tag. Then add the properties discussed in Table 9.3.
The persistence.xml configuration file appears in Listing 9.2.
Listing 9.2 The Persistence.xml Configuration File
<?xml version="1.0" encoding="UTF-8"?> <persistence version="2.0" 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"> <persistence-unit name="kundera"> <provider>com.impetus.kundera.KunderaPersistence</provider> <class>kundera.Catalog</class> <properties> <property name="kundera.nodes" value="localhost"/> <property name="kundera.port" value="9160"/> <property name="kundera.keyspace" value="Kundera"/> <property name="kundera.dialect" value="cassandra"/> <property name="kundera.client.lookup.class" value="com.impetus.client.cassandra.pelops.PelopsClientFactory" /> <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>
Some NoSQL database–specific properties may also be specified in persistence.xml file. For example, to configure Cassandra-specific properties, add the following property for the Cassandra-specific configuration file in persistence.xml:
<property name="kundera.client.property" value="kundera-cassandra.xml" />
The name of the Cassandra-specific configuration file, kundera-cassandra.xml, is arbitrary. Connection-, schema-, and table-specific properties may be specified. The connection-specific property that may be specified is cql.version
. Some of the schema-specific properties supported are discussed in Table 9.4.
The column family–specific properties supported by Cassandra are discussed in Table 9.5.
A sample Cassandra-specific configuration file appears in Listing 9.3.
Listing 9.3 Sample Cassandra-Specific Configuration File
<?xml version="1.0" encoding="UTF-8"?> <clientProperties> <datastores> <dataStore> <name>cassandra</name> <connection> <properties> <property name="cql.version" value="3.0.0"></property> </properties> </connection> <schemas> <schema> <name>KunderaCassandra</name> <properties> <property name="strategy.class" value="SimpleStrategy" /> <property name="replication.factor" value="1" /> <property name="durable.writes" value="true" /> </properties> <tables> <table> <name>catalog</name> <properties> <property name="default.validation.class" value="UTF8Type"></property> <property name="key.validation.class" value="UTF8Type"> </property> <property name="replicate.on.write" value="true"></property> <property name="comparator.type" value="UTF8Type"></property> </properties> </table> </tables> </schema> </schemas> </dataStore> </datastores> </clientProperties>
The Cassandra-specific configuration file is not required if you are using the default values for the properties and you have not used any Cassandra-specific configuration files in this chapter. The Cassandra-specific configuration file listed is provided as a sample if non-default values are to be configured.
You have configured a JPA project for object/relational mapping to the Cassandra database. Next, you will run some CRUD operations using the JPA API. First, however, you need to create a client class for the CRUD operations. You will use a Java class as a client class. Follow these steps:
1. Select File > New > Other.
2. In the New dialog box, select Java > Class. Then click Next, as shown in Figure 9.16.
3. In the New Java Class wizard, specify a package (kundera
) and a class name (KunderaClient
). Then select the method stub for the main method to add to the class. Finally, click Finish, as shown in Figure 9.17. The kundera.KunderaClient
class is added to the Kundera project, as shown in Figure 9.18.
In the next few sections, you will create a catalog. You have already created a catalog
table; next, you will add data to the catalog
table, find a catalog entry, update a catalog entry, and delete a catalog entry.
In this section, you will add some data to the catalog
column family in Cassandra. Add a method called create()
to the KunderaClient
class and invoke the method from the main
method so that the method is invoked when the application is run. The JPA API is defined in the javax.persistence
package. The EntityManager
interface is used to interact with the persistence context. The EntityManagerFactory
interface is used to interact with the entity manager factory for the persistence unit. The Persistence
class is used to obtain an EntityManagerFactory
object in a Java SE environment. Create an EntityManagerFactory
object using the Persistence
class static
method createEntityManagerFactory(java.lang.String persistenceUnitName)
. Create an EntityManager
instance from the EntityManagerFactory
object using the createEntityManager()
method.
EntityManagerFactory emf = Persistence.createEntityManagerFactory("kundera"); em = emf.createEntityManager();
In the create()
method, create an instance of the JPA entity class Catalog
. Using the set
methods, set the catalogId
, journal
, publisher
, edition
, title
, and author
fields.
Catalog catalog = new Catalog(); catalog.setCatalogId("catalog1"); catalog.setJournal("Oracle Magazine"); catalog.setPublisher("Oracle Publishing"); catalog.setEdition("November-December 2013"); catalog.setTitle("Engineering as a Service"); catalog.setAuthor("David A. Kelly");
Use the persist(java.lang.object entity)
method in the EntityManager
interface to make the domain model managed and persistent.
em.persist(catalog);
Similarly, other JPA instances may be persisted.
To run the KunderaClient application, right-click the KunderaClient.java file in the Package Explorer and select Run As > Java Application, as shown In Figure 9.19.
Three rows are added to the catalog
column family. In Cassandra-Cli, run the following command to list the entity instances persisted using Kundera to the catalog
column family:
list catalog;
The output lists the three rows added, as shown in Figure 9.20.
The EntityManager
class provides several methods for finding an entity instance. In this section, you will find a Catalog
entity instance using the find(java.lang.Class<T> entityClass, java.lang.Object primaryKey)
method in which the first parameter is the entity class and the second parameter is the primary key for the row to find. Add a method called findByClass()
to the KunderaClient
class and invoke the method from the main
method so that the method is invoked when the application is run. Invoke the find(java.lang.Class<T> entityClass, java.lang.Object primaryKey)
method using Catalog.class
as the first argument and "catalog1"
as the second argument.
Catalog catalog = em.find(Catalog.class, "catalog1");
Invoke the get
methods on the Catalog
instance to output the entity fields.
System.out.println(catalog.getJournal()); System.out.println(catalog.getPublisher()); System.out.println(catalog.getEdition()); System.out.println(catalog.getTitle()); System.out.println(catalog.getAuthor());
Run the KunderaClient application in the Eclipse IDE. The column values for the row with the primary key "catalog1"
are output, as shown in Figure 9.21.
The Query
interface is used to run a query in the Java Persistence query language and native SQL. The EntityManager
interface provides several methods for creating a Query
instance. In this section, you will run a Java Persistence query language statement by first creating an instance of Query
with the EntityManager
method createQuery(java.lang.String qlString)
and then invoking the getResultList()
method on the Query
instance. Add a method called query()
to the KunderaClient
class and invoke the method from the main
method so that the method is invoked when the application is run. In the query()
method, invoke the createQuery(java.lang.String qlString)
method to create a Query
instance. Supply the Java Persistence query language statement as SELECT c FROM Catalog c
.
javax.persistence.Query query = em.createQuery("SELECT c FROM Catalog c");
Invoke the getResultList()
method on the Query
instance to run the SELECT
statement and return a List<Catalog>
as the result.
List<Catalog> results = query.getResultList();
Iterate over the List
object using an enhanced for
statement to output the fields of the Catalog
instance.
for (Catalog catalog : results) { System.out.println(catalog.getCatalogId()); System.out.println(catalog.getJournal()); System.out.println(catalog.getPublisher()); System.out.println(catalog.getEdition()); System.out.println(catalog.getTitle()); System.out.println(catalog.getAuthor()); }
Run the KunderaClient application to output the result of the Java Persistence query language query, as shown in Figure 9.22.
All three rows are output as follows:
catalog1 Oracle Magazine Oracle Publishing November-December 2013 Engineering as a Service David A. Kelly catalog2 Oracle Magazine Oracle Publishing November-December 2013 Quintessential and Collaborative Tom Haunert catalog3 Oracle Magazine Oracle Publishing November-December 2013
In this section, you will update a catalog
entry using the Java Persistence API. The persist()
method in EntityManager
may be used to persist an updated entity instance. Add a method called update()
to the KunderaClient
class and invoke the method from the main
method so that it is invoked when the application is run. For example, to update the edition
column in the row with the primary key "catalog1"
, create an entity instance for the catalog1
row using the find(java.lang.Class<T> entityClass, java.lang.Object primaryKey)
method. Then set the edition
field to the updated value using the setEdition
method. Persist the updated Catalog
instance using the persist(java.lang.Object entity)
method.
Catalog catalog = em.find(Catalog.class, "catalog1"); catalog.setEdition("Nov-Dec 2013"); em.persist(catalog);
The Java Persistence query language provides the UPDATE
clause to update a row. Create a Query
instance using an UPDATE
statement and the createQuery(String)
method in EntityManager
. Then invoke the executeUpdate()
method to execute the UPDATE
statement.
em.createQuery("UPDATE Catalog c SET c.journal = 'Oracle-Magazine'").executeUpdate();
The journal
column in all the rows in the catalog
column family is updated. Having applied updates, invoke the query()
method to output the updated rows. The updated rows have the updated values, as shown in Figure 9.23.
The complete output for the updated rows is as follows:
catalog1 'Oracle-Magazine' Oracle Publishing Nov-Dec 2013 Engineering as a Service David A. Kelly catalog2 'Oracle-Magazine’ Oracle Publishing November-December 2013 Quintessential and Collaborative Tom Haunert catalog3 'Oracle-Magazine' Oracle Publishing November-December 2013
In this section, you will remove rows persisted in Cassandra using the Java Persistence API. The remove(java.lang.Object entity)
method in EntityManager
may be used to remove an entity instance. Add a method called delete()
to the KunderaClient
class and invoke the method from the main
method so that it is invoked when the application is run. To remove the row with the primary key "catalog1"
, create an entity instance for the catalog1
row using the find(java.lang.Class<T> entityClass, java.lang.Object primaryKey)
method. Then invoke the remove(java.lang.Object entity)
method to remove the catalog1
row from Cassandra.
Catalog catalog = em.find(Catalog.class, "catalog1"); em.remove(catalog);
Similarly, rows catalog2
and catalog3
may removed.
catalog = em.find(Catalog.class, "catalog2"); em.remove(catalog); catalog = em.find(Catalog.class, "catalog3"); em.remove(catalog);
The Java Persistence query language provides the DELETE
clause to delete a row. Create a Query
instance using a DELETE
statement and the createQuery(String)
method in EntityManager
. Then invoke the executeUpdate()
method to execute the DELETE
statement.
em.createQuery("DELETE FROM Catalog c").executeUpdate();
All rows are deleted. The DELETE
statement does not delete the row itself but deletes all the columns in the rows. Having performed the deletion, either using the remove(java.lang.Object entity)
method or the DELETE
Java Persistence query language statement, invoke the query()
method to output any Catalog
instances persisted to catalog
table. Because the catalog
table does not contain any persisted Catalog
instances, the NullPointerException
is generated as shown in Figure 9.24.
The rows in the catalog
column family may be listed in the Cassandra-Cli with the following command:
list catalog;
Empty rows are listed as the row columns are deleted, as shown in Figure 9.25.
The KunderaClient application appears in Listing 9.4.
Listing 9.4 The KunderaClient Application
package kundera; import java.util.HashMap; import java.util.List; import java.util.Map; import javax.persistence.EntityManager; import javax.persistence.EntityManagerFactory; import javax.persistence.Persistence; import javax.persistence.PersistenceContext; import javax.persistence.PersistenceContextType; import javax.persistence.PersistenceUnit; public class KunderaClient { private static EntityManager em; private static EntityManagerFactory emf; public static void main(String[] args) { emf = Persistence.createEntityManagerFactory("kundera"); em = emf.createEntityManager(); create(); // findByClass(); // query(); // update(); //delete(); } private static void create() { Catalog catalog = new Catalog(); catalog.setCatalogId("catalog1"); catalog.setJournal("Oracle Magazine"); catalog.setPublisher("Oracle Publishing"); catalog.setEdition("November-December 2013"); catalog.setTitle("Engineering as a Service"); catalog.setAuthor("David A. Kelly"); em.persist(catalog); catalog = new Catalog(); catalog.setCatalogId("catalog2"); catalog.setJournal("Oracle Magazine"); catalog.setPublisher("Oracle Publishing"); catalog.setEdition("November-December 2013"); catalog.setTitle("Quintessential and Collaborative"); catalog.setAuthor("Tom Haunert"); em.persist(catalog); catalog = new Catalog(); catalog.setCatalogId("catalog3"); catalog.setJournal("Oracle Magazine"); catalog.setPublisher("Oracle Publishing"); catalog.setEdition("November-December 2013"); catalog.setTitle(""); catalog.setAuthor(""); em.persist(catalog); } private static void findByClass() { Catalog catalog = em.find(Catalog.class, "catalog1"); System.out.println(catalog.getJournal()); System.out.println(" "); System.out.println(catalog.getPublisher()); System.out.println(" "); System.out.println(catalog.getEdition()); System.out.println(" "); System.out.println(catalog.getTitle()); System.out.println(" "); System.out.println(catalog.getAuthor()); } private static void query() { javax.persistence.Query query = em .createQuery("SELECT c FROM Catalog c"); List<Catalog> results = query.getResultList(); if(results != null) { for (Catalog catalog : results) { System.out.println(catalog.getCatalogId()); System.out.println(" "); System.out.println(catalog.getJournal()); System.out.println(" "); System.out.println(catalog.getPublisher()); System.out.println(" "); System.out.println(catalog.getEdition()); System.out.println(" "); System.out.println(catalog.getTitle()); System.out.println(" "); System.out.println(catalog.getAuthor()); } } } private static void update() { Catalog catalog = em.find(Catalog.class, "catalog1"); catalog.setEdition("Nov-Dec 2013"); em.persist(catalog); em.createQuery("UPDATE Catalog c SET c.journal = 'Oracle- Magazine'") .executeUpdate(); /* * em.createQuery( * "UPDATE Catalog c SET c.author = 'Kelly, David A.' WHERE c.catalogId='catalog1'" * ) .executeUpdate(); update with WHERE does not get applied. */ System.out.println("After updating"); System.out.println(" "); query(); } private static void delete() { Catalog catalog = em.find(Catalog.class, "catalog1"); em.remove(catalog); catalog = em.find(Catalog.class, "catalog2"); em.remove(catalog); catalog = em.find(Catalog.class, "catalog3"); em.remove(catalog); System.out.println("After removing catalog3"); query(); /* * em.createQuery( * "DELETE FROM Catalog c WHERE c.title='Engineering As a Service'") * .executeUpdate(); System.out.println(" "); // * System.out.println("After removing catalog1"); query(); */ // DELETE with WHERE does not get applied. em.createQuery("DELETE FROM Catalog c").executeUpdate(); System.out.println(" "); System.out.println("After removing all catalog entries"); query(); } private static void close() { em.close(); // emf.close(); } }
The JPA is designed for relational databases, but the Kundera library provides object/relational mapping using the JPA for NoSQL data stores Cassandra, MongoDB, and HBase. In this chapter, you used the Java Persistence API with Kundera to run CRUD operations on Cassandra. In the next chapter, you will use the Spring Data project with Apache Cassandra.
3.138.37.191