EclipseLink JPA and Oracle NoSQL Database (ONDB)

Back in 2005, I was the Project Lead for JSR220-ORM tooling in Eclipse. Sun’s JSR220 project was the early POJO persistence standard for Java that became the EBJ 3.0 spec, the predecessor of the JPA Interface found today in every Java download.

In the same timeframe, Oracle announced it was joining the Eclipse Foundation and in that process launching a competing JSR220 tooling project called Dali. Given two projects doing the exact same thing, it did not take long for the JSR220-ORM and Dali project teams to merge into a single project. Eventually, the Oracle team took lead on Dali and marched into the future.

Here I am now 8 years later at Oracle helping drive the standardization of NoSQL technology. JPA presents a great abstraction layer for dealing with database persistence, allowing users of Java to persist their application objects with literally the push of a button.  Plus, when using JPA with a NoSQL database, it allows the developer to use a soft schema approach to application development, where the data model is driven from the application space rather than the database design and evolution of the application can occur much more rapidly.  In fact, in 2005 when I was V.P. Technology for a NoSQL Database company, one of the things we did was create a JPA interface for standards based access to our NoSQL store, the reason we launched that JSR220 tool project in Eclipse. So, I thought I would poke around a little bit with the Oracle NoSQL Database (ONDB) and JPA interfaces. To my surprise, I found that some folks had already made a great start down that path….pretty cool.

There is an EclipseLink plugin that supports NoSQL Databases , including ONDB.

The examples on the EclipseLink site don’t include ONDB and I found that a number of things need to change in order to get things working, so thought I would provide a little tutorial. I am using Windows, but the code changes in the example are the same for Linux, just download and setup Linux versions of the software.

Getting the software and example code:

Download ONDB and unzip / untar it into a directory of your choice, here I downloaded kv-ce-2.0.39.zip and will use a directory /kv-2.0.39

You need to get the correct stuff for Eclipse and EclipseLink. EclipseLink NoSQL support relies on JCA, so you need the IDE for Java EE Developers edition. If you are going to use the Eclipse Juno release, then you should also download and install EclipseLink 2.4.2. scroll down a bit to see it. If you want to go with the bleeding edge as I’ve done, then Eclipse Kepler and the latest EclipseLink 2.5 is the way to go.

Now at first glance it looks like EclipseLink 2.5 includes support for NoSQL databases right out of the box, but in fact if you look closely there is another specific NoSQL plugin download necessary. Download, unzip and save this plugin in a directory e.g. /nosql-plugin . Later you’ll need to get some libraries from it.

Finally, get the example code discussed here from the Gitub repository. In the lower right hand corner, you can download it as a zip file, then unzip it into a directory for import into your Eclipse project later on e.g. directory /example/nosql-master/

What you really need to know to create JPA applications that use ONDB:

I will give a complete breakdown of how to setup the software and install all of the proper project libraries in the following pages. However, except for kicking off an instance of ONDB, a lot of the configuration is routine for an Eclipse user. So, let me summarize here the main points and highlight some important code tips for you to check out when things are up and running.

Required Project Libraries:

EclipseLink 2.5.0 - includes: eclipselink.jar, javax.persistence.*, org.eclipse.persistence.*, others kvclient.jar, connector.jar, javax.resource_1.5.0*.jar, org.eclipse.persistence.nosql_2.5.0*.jar, org.eclipse.persistence.oracle.nosql_2.5.0*.jar

Every JPA project needs a persistence.xml file (found under the META-INF directory) that describes what is called a persistence-unit: connection configuration, mapping and other aspects of a JPA enabled project. Below is one configured for an ONDB NoSQL database and you should pay special attention to all the “properties” settings as these are unique to the Oracle NoSQL Database. In particular property.nosql.host = database_host:port and property.nosql.store=name_of_nosql_database which could change depending on how/where you’ve installed your ONDB database server.

<?xml version="1.0" encoding="UTF-8" ?>

<persistence xmlns:xsi="…lots of cryptic namespace stuff…..">

<persistence-unit name="ondb" transaction-type="RESOURCE_LOCAL">

<class>model.Order</class>

<class>model.OrderLine</class>

<class>model.Address</class>

<class>model.Customer</class>

<properties>

<property name="eclipselink.target-database" value="org.eclipse.persistence.nosql.adapters.nosql.OracleNoSQLPlatform"/>

<property name="eclipselink.nosql.connection-spec" value="org.eclipse.persistence.nosql.adapters.nosql.OracleNoSQLConnectionSpec"/>

<property name="eclipselink.nosql.property.nosql.host" value="RCGREENE-LAP:5000"/>

<property name="eclipselink.nosql.property.nosql.store" value="kvstore"/>

<property name="eclipselink.logging.level" value="FINEST"/>

</properties>

</persistence-unit>

</persistence>

Mappings:

In order to specify the classes in Java that will get persisted into the NoSQL database, they must be marked for persistence using annotations or equivalent XML specification in mapping files. They key annotations are summarized as follows:

All classes that will be stored must have the @NoSQL annotation. Top level classes that have identity need the @Entity annotation, second level nested classes that are references from and denormalized into a parent top level object need the @Embedded annotation. Notice below how attributes of a class that are stored use the annotations @Basic and @Embedded to indicate if the attribute is storing a basic string or some other more complex nested type. The following code snippets are not straight from the example code, but a modification to illustrate the points about annotations. What is really cool about using JPA for your persistence API on top of a NoSQL database, is that the JPA runtime knows how to leverage the key’s in a key-value store to automatically handle both aggregations e.g. collections like ArrayList<> and linking of objects e.g. Order hasA Customer. This is done using attribute mapping annotations @ElementCollection and @ManytoOne respectively. It is a kind of way of getting JOIN behavior without having to explicitly execute a JOIN operation in the database, it is hardwired –vs- calculated so it’s a lot faster. For complete detail on the API, see the reference documentation and examples.

/**

* Customer, stored as a root JSON object.

* @author James Sutherland, Robert Greene

*/

@Entity

@NoSql(dataFormat=DataFormatType.MAPPED)

public class Customer implements Serializable {

/* The id uses the generated OID (UUID). */

@Id

@GeneratedValue

private String id;

@Basic

private String name;

/* A nested embeddable value is stored as Embedded. */

@Embedded

private Address address;

/**

* Address, stored as part of the Order document.

* @author James Sutherland, Robert Greene

*/

@Embeddable

@NoSql(dataFormat=DataFormatType.MAPPED)

public class Address implements Serializable {

private String street;

private String city;

private String province;

private String country;

private String postalCode;

Using the JPA runtime:

In order to take advantage of the above persistence-unit, which you should note has the name=”ondb” , you need to use it in a factory pattern. Units of work (a.k.a. transactions), will follow this similar pattern which you will see in the Test.java file of the example.

EntityManagerFactory factory = Persistence.createEntityManagerFactory("ondb");

EntityManager em = factory.createEntityManager();

em.getTransaction().begin();

Customer customer = new Customer();

customer.setName("AMCE");

em.persist(customer);

em.getTransaction().commit();

em.close();

Given the basics of what’s been described above, you should be well on your way to being able to use JPA with a NoSQL Database. Now we take you step by step thru a complete install and run.

Installing the Eclipse Software, create a project and set up the environment:

Using Kepler, when installing EclipseLink, I found it difficult to get the normal Help>>Install New Software to work correctly, perils of using the latest stuff, might work for you. However, when I just created a new JPA Project it gives the option to automatically install the EclipseLink runtime. So, within Eclipse, select File>>New>>JPA Project, give it a name (ONDB) and go thru the dialog boxes, selecting jre6 as your target runtime and accepting everything else as default until it wants you to select a “JPA Facet” and gives you the option to download the library by clicking the lower icon (see image below) and that seemed to work just fine.

JPAFacet

A dialog box will pop up with all of the libraries found for EclipseLink, just chose version 2.5.0 and let it install. Afterward, you don’t need to worry about the other options like setting the connection, just click the finish button and you’re done.

Add the ONDB libraries to your project. Select your ONDB project, right click and select Properties. Then in the dialog as seen below, choose Add External JARs.

JavaBuildPath

Put plugin on project path: Click on Add External Jars and navigate to the directory where you unzipped the nosql plugin e.g. /nosql-plugin and select and click OK for the 3 .jar files: javax.resource_1.5.0.v200906010428, org.eclipse.persistence.nosql_2.5.0.v20130507-3faac2b, org.eclipse.persistence.oracle.nosql_2.5.0.v20130403-746f2e3

Put ONDB client on project path: Click on Add External Jars and navigate to the directory where you installed ONDB software, then to the lib directory and select the kvclient.jar file e.g. /kv-2.0.39/lib/kvclient.jar. Click OK to load that .jar and its dependents into your project and click OK.

Your workspace will look like the following:

ProjectLibs

Last step, import the example code. Select the source folder from you ONDB Project and right click and select “import” as shown below.

ExampleImport

In the dialog box that pops us choose General>>File System option and in the file browser, navigate to the src directory of where you unzipped the example code e.g. /nosql-master/src and select that directory for import. Then make sure to select the /src resource to select all of the content in that directory per the image below.

FileImport

The final Eclipse workspace image should appear as the following:

Project

Setup and run an Oracle NoSQL Database (ONDB) instance for the application:

This is pretty straight forward, open a command prompt shell make sure you have Java in your path by executing the following and make sure a minimum 1.6 version is found.

C:\Users\rcgreene.ORADEV>java –version

java version "1.6.0_43"

Java(TM) SE Runtime Environment (build 1.6.0_43-b01)

Java HotSpot(TM) 64-Bit Server VM (build 20.14-b01, mixed mode)

Now navigate the the lib directory where you unzipped the ONDB download e.g. /kv-2.0.39/lib execute the following command from the lib directory (cmd>java –jar kvstore.jar kvlite) and an instance of ONDB will be created and started. Note – this is the lightweight instance of ONDB suitable for proof of concept and prototyping, the setup of a full distributed cluster is beyond the scope of this tutorial. Starting up the lightweight version should look like the following screen shot.

command

Running the example application:

Now you are ready to run the code. All you need to do is go back to Eclipse and navigate to your ONDB Project and find ( src/examples/Test.java ) and select the Test.java file.

Right click on Test.java and select the option to Run As>>Java Application. You will get a bunch of output similar to the following:

[EL Finer]: transaction: 2013-07-06 15:40:26.041--ClientSession(1104444203)--Connection(2084240281)--Thread(Thread[main,5,main])--begin transaction

[EL Fine]: sql: 2013-07-06 15:40:26.042--ClientSession(1104444203)--Connection(2084240281)--Thread(Thread[main,5,main])--Executing MappedInteraction()

spec => null

properties => {nosql.operation=PUT_IF_ABSENT}

input => [DatabaseRecord(

CUSTOMER.ID => 3A5BB7A7-00D5-4850-B1CA-2C181EA02029

CUSTOMER.NAME => Smith)]

[EL Finest]: query: 2013-07-06 15:40:26.057--ClientSession(1104444203)--Thread(Thread[main,5,main])--Adapter result: null

[EL Finest]: query: 2013-07-06 15:40:26.058--ClientSession(1104444203)--Thread(Thread[main,5,main])--Data access result: null

[EL Finest]: query: 2013-07-06 15:40:26.058--UnitOfWork(158363048)--Thread(Thread[main,5,main])--Execute query InsertObjectQuery(model.Customer@342f356f)

[EL Fine]: sql: 2013-07-06 15:40:26.058--ClientSession(1104444203)--Connection(2084240281)--Thread(Thread[main,5,main])--Executing MappedInteraction()

spec => null

properties => {nosql.operation=PUT_IF_ABSENT}

input => [DatabaseRecord(

CUSTOMER.ID => E6A97438-CA87-4244-8935-F41AD9102D6D

CUSTOMER.NAME => AMCE)]

[EL Finest]: query: 2013-07-06 15:40:26.06--ClientSession(1104444203)--Thread(Thread[main,5,main])--Adapter result: null

[EL Finest]: query: 2013-07-06 15:40:26.06--ClientSession(1104444203)--Thread(Thread[main,5,main])--Data access result: null

[EL Finest]: query: 2013-07-06 15:40:26.06--UnitOfWork(158363048)--Thread(Thread[main,5,main])--Execute query InsertObjectQuery(Order(Pingpong table, 402.0))

[EL Fine]: sql: 2013-07-06 15:40:26.061--ClientSession(1104444203)--Connection(2084240281)--Thread(Thread[main,5,main])--Executing MappedInteraction()

spec => null

properties => {nosql.operation=PUT_IF_ABSENT}

input => [DatabaseRecord(

ORDER.ID => FA17719F-4ADB-412C-AF91-749E0908A13C

ORDER.TOTALCOST => 402.0

ORDER.DESCRIPTION => Pingpong table

ORDER.VERSION => 1

ORDER.CUSTOMER_ID => 3A5BB7A7-00D5-4850-B1CA-2C181EA02029

ORDER.BILLINGADDRESS => [DatabaseRecord(

POSTALCODE => L5J1H8

STREET => 7 Bank St.

PROVINCE => ON

CITY => Ottawa

COUNTRY => Canada)]

ORDER.ORDERLINES => [DatabaseRecord(

DESCRIPTION => table

LINENUMBER => 1

COST => 300.0), DatabaseRecord(

DESCRIPTION => balls

LINENUMBER => 2

COST => 5.0), DatabaseRecord(

DESCRIPTION => rackets

LINENUMBER => 3

COST => 15.0), DatabaseRecord(

DESCRIPTION => net

LINENUMBER => 4

COST => 2.0), DatabaseRecord(

DESCRIPTION => shipping

LINENUMBER => 5

COST => 80.0)]

ORDER.SHIPPINGADDRESS => [DatabaseRecord(

POSTALCODE => L5J1H7

STREET => 17 Jane St.

PROVINCE => ON

CITY => Ottawa

COUNTRY => Canada)])]

Summary:

Hopefully this has been useful for you. It is great to be able to get some of the advantages of using a NoSQL database like ONDB to achieve scale-out and extremely high throughput writes combined with highly concurrent low latency reads while at the same time being able to use a higher level API. Using a JPA abstraction takes care of a lot of the details which are normally left to the user to code when using a key-value type of NoSQL store. What’s also really cool is that unlike with a relational database, you can change the schema without having to setup a change request with a DBA. Just change your Java classes and recompile and you’re off and running with a new version of the application. I will cover that process as a future topic. 

Comments:

Hi
I tried same program using EclipseLink. but elementcollection is not stored. ONDB is storing only the first element of list.
And also I noted select JPQL queries are also failing with ONDB.
Gopi

Posted by guest on April 04, 2014 at 11:51 AM PDT #

It looks like something has been broken in the Eclipse driver. Recently I found that Kunder's JPA seems be be a much better implementation over Oracle NoSQL. Perhaps give this one a try: https://github.com/impetus-opensource/Kundera

Posted by guest on April 04, 2014 at 12:06 PM PDT #

Post a Comment:
  • HTML Syntax: NOT allowed
About

This blog is about everything NoSQL. An open place to express thoughts on this exciting topic and exchange ideas with other enthusiasts learning and exploring about what the coming generation of data management will look like in the face of social digital modernization. A collective dialog to invigorate the imagination and drive innovation straight into the heart of our efforts to better our existence thru technological excellence.

Search

Categories
Archives
« April 2014
SunMonTueWedThuFriSat
  
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
   
       
Today