JPA is the Java standard for persistence and different vendors can implement their own implementation of this API and they can (and do) add proprietary extensions. Three of the most common implementations are OpenJPA, Hibernate and Toplink. JPA can be used within server containers or outside of them (i.e. with either J2EE or J2SE).

Typically a JPA implementation would access the database (for example, MySQL Cluster) using JDBC. JDBC gives a great deal of flexibility to the JPA implementer but it cannot give the best performance when using MySQL Cluster as there is an internal conversion to SQL by Connector/J and a subsequent translation from SQL to the C++ NDB API by the MySQL Server. As of MySQL Cluster 7.1, OpenJPA can be configured to use the high performance NDB API (via ClusterJ) for most operations but fall back on JDBC for more complex queries.

The first implementation of ClusterJPA is as an OpenJPA BrokerFactory but in the future, it may be extended to work with other JPA implementations.

ClusterJPA overcomes ClusterJ limitations, notably:

Persistent classes

Relationships

Joins in queries

Lazy loading

Table and index creation from object model

Fig.2 ClusterJPA Performance

Typically users base their selection of a JPA solution on factors such as proprietary extensions, what existing applications already use and (increasingly with ClusterJPA) performance.

The performance of ClusterJPA (OpenJPA using ClusterJ) has been compared with OpenJPA using JDBC in Figure 2. It should be noted that the performance is significantly better when using ClusterJPA (the yellow bar). It is hoped that in the future the performance can be improved even further for finds, updates and deletes.

Adapting an OpenJPA based application to use ClusterJPA with MySQL Cluster should be fairly straight-forward with the main change being in the definition of the persistence unit in persistence.xml:

Defining the object-to-table mappings is performed by annotating the persistent class for the domain object. If not already in existence, OpenJPA will create the table. The property openjpa.jdbc.DBDictionary tells OpenJPA to create the tables using ndb as the storage engine.

For this tutorial, it is necessary to have MySQL Cluster up and running. For simplicity all of the nodes (processes) making up the Cluster will be run on the same physical host, along with the application.

Although most of the database access is performed through the NDB API, the Cluster includes a MySQL Server process for OpenJPA to use for complex queries and to allow the user to check the contents of the database manually.

These are the MySQL Cluster configuration files being used :

config.ini:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

<span style="color: #3366ff;">[ndbd default]

noofreplicas=2

datadir=/home/billy/mysql/my_cluster/data

[ndbd]

hostname=localhost

id=3

[ndbd]

hostname=localhost

id=4

[ndb_mgmd]

id=1

hostname=localhost

datadir=/home/billy/mysql/my_cluster/data

[mysqld]

hostname=localhost

id=101

[api]

hostname=localhost</span>

my.cnf:

1

2

3

4

<span style="color: #3366ff;">[mysqld]

ndbcluster

datadir=/home/billy/mysql/my_cluster/data

basedir=/usr/local/mysql</span>

This tutorial focuses on ClusterJPA rather than on running MySQL Cluster; if you are new to MySQL Cluster then refer to Running a simple Cluster before trying these tutorials.

JPA/OpenJPA/ClusterJPA can be used within or outside a container (i.e. it can be used with J2EE or J2SE) – for simplicity, this tutorial does not use a container (i.e. it is written using J2SE).

Before being able to run any ClusterJPA code, you first need to download and install OpenJPA from http://openjpa.apache.org/ – this tutorial uses OpenJPA 1.2.1. Simply extract the contents of the binary tar ball to the host you want to run your application on; for this tutorial, I use /usr/local/openjpa.

Additionally, ClusterJPA must sometimes use JDBC to satisfy certain queries and so “JDBC Driver for MySQL (Connector/J)” should also be installed – this can be downloaded from http://dev.mysql.com/downloads/connector/j/ Again, simply extract the contents of the tar ball, for this tutorial the files are stored in /usr/local/connectorj and version 5.1.12 is used.

If the ClusterJ tutorial has already been run on this MySQL Cluster database then drop the tables from the cluster so that you can observe them being created automatically – though in a real application, you may prefer to create them manually.

A configuration file is required to indicate how persistence is to be handled for the application. Create a new directory called META-INF in the application source directory and within there create a file called persistence.xml:

A persistence unit called ‘clusterdb’ is created; the provider (implementation for the persistence) is set to openjpa (as opposed for example to hibernate). Two classes are specified – ‘Employee’ and ‘Department’ which relate to the persistent classes that the application will define. Connector/J is defined as the JDBC connection (together with the host and the port of the MySQL Server to be used). The key to having OpenJPA use ClusterJPA is to set the BrokerFactory to ndb and specify the connect string (host:port) for the MySQL Cluster management node. The database is defined to be ‘clusterdb’ for both the JDBC and ClusterJ connections. The engine type when creating tables is set to ndb.

If not already done so, create the ‘clusterdb’ database (if it already contains tables from the ClusterJ tutorial then drop them):

1

<span style="color: #800000;">mysql>create database clusterdb;</span>

The next step is to create the persistent class definitions for the Department and Employee Entities:

Department.java:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

<span style="color: #3366ff;">import javax.persistence.*;

@Entity(name="department")

publicclassDepartment{

privateintId;

privateStringSite;

publicDepartment(){}

@Id publicintgetId(){returnId;}

publicvoidsetId(intid){Id=id;}

@Column(name="location")

publicStringgetSite(){returnSite;}

publicvoidsetSite(Stringsite){Site=site;}

publicStringtoString(){

return"Department: "+getId()+" based in "+getSite();

}

}</span>

Using the @Entity tag, the table name is specified to be ‘department’. Note that unlike ClusterJ, ClusterJPA uses persistent classes (rather than interfaces) and so it is necessary to define the properties as well as the getter/setter methods. The primary key is defined using the @Id tag and we specify that the column associated with the Site property should be called ‘location’ using the @Column tag.

As this is a class, it is possible to add other useful methods – in this case toString().

Employee.java:

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

31

32

33

34

35

36

37

38

39

40

41

<span style="color: #3366ff;">import javax.persistence.*;

@Entity(name="employee")//Name of the table

publicclassEmployee{

privateintId;

privateStringFirst;

privateStringLast;

privateStringCity;

privateStringStarted;

privateStringEnded;

privateintDepartment;

publicEmployee(){}

@Id publicintgetId(){returnId;}

publicvoidsetId(intid){Id=id;}

publicStringgetFirst(){returnFirst;}

publicvoidsetFirst(Stringfirst){First=first;}

publicStringgetLast(){returnLast;}

publicvoidsetLast(Stringlast){Last=last;}

@Column(name="municipality")

publicStringgetCity(){returnCity;}

publicvoidsetCity(Stringcity){City=city;}

publicStringgetStarted(){returnStarted;}

publicvoidsetStarted(Stringdate){Started=date;}

publicStringgetEnded(){returnEnded;}

publicvoidsetEnded(Stringdate){Ended=date;}

publicintgetDepartment(){returnDepartment;}

publicvoidsetDepartment(intdepartment){Department=department;}

publicStringtoString(){

returngetFirst()+" "+getLast()+" (Dept "+

getDepartment()+") from "+getCity()+

" started on "+getStarted()+" & left on "+getEnded();

}

}</span>

The next step is to write the application code which we step through here block by block; the first of which simply contains the import statements and then:

<span style="color: #3366ff;">System.out.println("The tables will now have been created - check through SQL.");

System.out.println("mysql> use clusterdb;");

System.out.println("mysql> show tables;");

System.out.println("Hit return when you are done");

Stringignore=br.readLine();</span>

As part of creating the EntityManagerFactory and EntityManager, OpenJPA creates the tables for the two classes specified for the ‘clusterdb’ persistence unit. While the application waits for the user to press return, this can be checked:

1

2

3

4

5

6

7

8

<span style="color: #800000;">mysql>useclusterdb

mysql>show tables;

+---------------------+

|Tables_in_clusterdb|

+---------------------+

|department|

|employee|

+---------------------+</span>

After hitting return, the application can create an Employee object and then persist it – at which point it will be stored in the ‘employee’ table. A second Employee object is then created and populated with the data read back from the database (using a primary key look up on the Id property with a value of 1):

After hitting return, the application continues and an update is made to the persisted Employee object – note that there is no need to explicitly ask for the changes to be persisted, this happens automatically when the transaction is committed:

Main.java (part 3):

1

2

3

4

<span style="color: #3366ff;">userTransaction.begin();

theEmployee.setCity("London");

theEmployee.setDepartment(777);

userTransaction.commit();</span>

1

2

3

<span style="color: #3366ff;">System.out.println("Chance to check the City is set in the database");

System.out.println("Hit return when you are done");

ignore=br.readLine();</span>

At this point, the application again waits while the user has a chance to confirm that the changes did indeed get written through to the database:

When allowed to continue, the application creates and persists an additional 100 Employee & Department entities. It then goes on to create and execute a query to find all employees with a department number of 777 and then looks up the location of the site for that department.

There is no date for when an NDB API plugin will be developed for TopLink but of course you can use it with MySQL Cluster already – it’s just that it will go through the JDBC/MySQL Server path and so the performance probably won’t be as good.

What could be the problem if after persistence.xml file is read the following exception is occured:
Exception in thread “Thread-0” java.lang.NoSuchMethodError: org.apache.openjpa.l
ib.conf.IntValue.get()I
at com.mysql.clusterj.openjpa.NdbOpenJPAConfigurationImpl.getConnectDela
y(NdbOpenJPAConfigurationImpl.java:155)