JDO facilitates rapid development with an open standard and can be used in nearly any Java environment. If your project has a limited budget and needs to be released within a relatively short period of time with as few bugs as possible, JDO may be a strong choice for handling persistence. Let's break down some key characteristics of JDO in more detail:
- JDO 2.0 is a standard. One advantage that JDO has over other
Java object-to-relational mapping frameworks is that it is part of the
Java Community Process
(http://www.jcp.org) as Java
Specification Request (JSR) 243. It is a standard API for which anyone
can create an implementation (in this article we use the JPOX 1.1
implementation). If you want to move to a different JDO
implementation—commercial or open source—you can do so without
changing any of the your code accessing the JDO API (assuming no
JPOX-specific extensions are used which are not part of the JDO 2.0
standard).
- It works with whatever Java 1.3+ environment you have. JDO is
designed to be used anywhere Java (1.3 or higher) is used including in
a J2EE Web or EJB container. JDO works with most popular databases
that support JDBC and can map to schemas which are already in place.
See
http://www.jpox.org/docs/1_1/rdbms.html
for a supported database listing for JPOX. DB2 is one of the many
databases on this list.
- Little training is needed to learn JDO. As this article shows,
no knowledge beyond basic Java skills is needed to create and persist
JDO objects.
- JDO reduces release cycles and increases software quality for
persistence code. Complex query strings, normal form
optimization, and the procedural nature of SQL are abstracted via JDO,
allowing a development team to focus on the core of their
application's persistence needs. Since more of what otherwise would be
application code is handled transparently by the JDO libraries, it can
reduce bugs and time to market.
- JDO has matured. You may be thinking, "Can it really work my
backend environment?" The JDO 2.0 specification introduced most
advanced persistence functionality that production systems require,
including development of very rich queries (including conventional SQL
if needed) and object-relational mapping technology which can use
existing database schemas.
- JPOX is released under the Apache Public License. This is a
very friendly license which provides a lot of freedom to those who
develop with the JPOX framework. This is not always the case, for
instance, Hibernate, another popular ORM framework, uses the GNU
Lesser General Public License (LGPL) which is more restrictive,
especially if you intend to patent your work (see
http://www.gnu.org/copyleft/lesser.html).
- Be aware of the trade-offs. When compared to other persistence
technologies such as Container Managed Persistence (CMP) and JDBC,
current JDO providers certainly are not first in its class for
support. Although the JDO specification is making progress, it is
still does not have the wide adoption and is several releases behind
other persistence technologies such as entity beans.
You'll need the following software in order to try out the implementation described in this article:
- Java SDK 1.3 or higher
- IBM DB2 V8.x or higher (A free 90-day trial is available from the
developerWorks downloads page.)
The following files are required:
- JPOX JAR files. JPOX's implementation of the JDO 2.0
specification is encapsulated in a JAR. Download
jpox-1.1.0-beta-3.jar(or later) from http://www.jpox.org/docs/download.html. Also download JPOX-enhancer-1.1.0-beta-3.jar (or later) from the same Web site. JPOX enhancer is a tool that automates the monotonous task of writing the methods JDO needs to persist each of your objects (explained later). - JDO 2.0 JAR. This jar contains the JDO 2.0 API or the
javax.jdo.*interfaces. Downloadjdo-2.0.jarfrom the JPOX download site above. It can be found near the bottom of the list under the heading "Sun JDO 2.0." - Apache Log4J JAR file. JPOX uses Apache Log4J to log events and
errors. Download
logging-log4j-1.2.9.jar(or later) from http://logging.apache.org/log4j (it is in the/dist/libdirectory of the provided zip file). - Apache BCEL JAR file. The JPOX enhancer requires the Apache
byte code engineering library (BCEL). Download
bcel-5.1.jar(or later) from http://jakarta.apache.org/bcel/.
For simplicity, the above JAR files have been renamed in this article as follows. It is recommended that you do the same so various commands can be pasted from this article and used without modification:
jpox.jar
jpox-enchancer.jar
jdo-2.0.jar
log4j.jar
bcel.jar - DB2 JARs with Type 4
driver.
db2jcc.jaranddb2jcc_license_cu.jarare needed. They are in the following directory by default:
Linux or UNIX®:/opt/IBM/db2/V8.x/java
Windows®:c:\program files\IBM\SQLLIB\java - Directories. Finally, create the following directory
structure:
/jdo-example-— Put all seven jar files in this directory.
/jdo-example/src/example-— This directory will include all source files (*.java).
/jdo-example/classes/example-— This directory will include all binary files (*.class).
/jdo-example/admin-— This directory will include configuration and deployment related files.
Start by deciding what to persist
For this article we will persist the information for an employee in a mock payroll system. There are two high-level approaches to designing persistence objects with JDO: top-down and bottom-up. In a top-down approach you design the Java objects that your application needs and let JDO generate the database schema and perform the object-to-relational mapping. For an example of a top-down approach, see the JPOX 1.1 Tutorial (see Resources).
This article uses a bottom-up approach which takes a schema already in place and creates JDO's object-to-relational mapping to corresponding persistence capable Java objects. The data that we will persist is the employee’s social security number, first name, last name, and salary. The database table with the employee's social as the primary key follows:
Figure 1. Employee Database Table
Once all necessary decisions are made for what to persist and what approach
to use, the next step is to create a DB2 database and an
EMPLOYEE table. Start the DB2 command line
processor. By default this tool can be found in the following
location:
/opt/IBM/db2/V8.x/bin/db2
c:\Program Files\IBM\SQLLIB\bin\db2.exe
Windows requires that the db2cmd.exe command be
run first in order to setup the shell environment for the command line
processor. Once the command-line processor is started, execute the
following commands:
Listing 1. DB2 commands to setup EMPLOYEE table
db2 => db2start db2 => create database MYDB db2 => connect to MYDB user db2admin using db2admin db2 => create table EMPLOYEE(SOCIAL bigint not null, FIRST_NAME varchar(20), LAST_NAME varchar(20), SALARY real, primary key(SOCIAL)) db2 => commit |
Notice the assumption made that db2admin is the
username and password for the database administrator.
The next step is to create the object(s) to be persisted. When writing a
class that will be persisted by JDO, the type of the class’s datamembers
is of significant consideration. All Java primitive types
(int, char,
float, etc.) are supported as well as most Java
objects in the java.util library (for example,
java.util.Collection and
java.util.Map). In the case where an object you
would like to persist is not supported, you can define your own. For a
list of innately supported types for JPOX see
http://www.jpox.org/docs/1_1/types.html.
Now let's write the Java class that will be persisted in the
EMPLOYEE table. Create the following class and
put it in the jdo-example/src directory:
Listing 2. Employee.java
package example;
public class Employee {
private long social;
private String firstName;
private String lastName;
private float salary;
/**
* Constructor
*/
Employee( long social, String firstName, String lastName, float salary ) {
this.social = social;
this.firstName = firstName;
this.lastName = lastName;
this.salary = salary;
}
void setSocial(long social) {
this.social = social;
}
long getSocial() {
return social;
}
void setFirstName(String firstName) {
this.firstName = firstName;
}
String getFirstName() {
return firstName;
}
void setLastName(String lastName) {
this.lastName = lastName;
}
String getLastName() {
return lastName;
}
void setSalary(float salary) {
this.salary = salary;
}
float getSalary() {
return salary;
}
}
|
Compile the object to be persisted with your favorite build tool, or you
can use the following command in a shell from the
jdo-example directory:
> javac -d classes src/example/Employee.java
Create the XML metadata for the object-relational mapping
JDO requires an XML file to define what fields are to be persisted and to
what JDBC or JDO wrapper constructs they should be mapped to. This file
defines the mapping from the EMPLOYEE database
table to the Employee Java object as
follows:
Figure 2. Object-Relational Mapping
Create an XML file called package.jdo with the
following content and put it in the
jdo-example/admin directory:
Listing 3. package.jdo
<?xml version="1.0"?>
<!DOCTYPE jdo PUBLIC "-//Sun Microsystems, Inc.//DTD Java Data Objects Metadata 2.0
//EN" "http://java.sun.com/dtd/jdo_2_0.dtd">
<jdo>
<package name="example">
<class name="Employee" identity-type="application" table="EMPLOYEE">
<field name="social" primary-key="true">
<column name="SOCIAL" jdbc-type="BIGINT"/>
</field>
<field name="firstName">
<column name="FIRST_NAME" length="20" jdbc-type="VARCHAR"/>
</field>
<field name="lastName">
<column name="LAST_NAME" length="20" jdbc-type="VARCHAR"/>
</field>
<field name="salary">
<column name="SALARY" jdbc-type="REAL"/>
</field>
</class>
</package>
</jdo>
|
To better understand the mapping process, let's take a look at the
class tag:
<class name="Employee" identity-type="application" table="EMPLOYEE">
This includes mapping information at the table level. In addition to the
Java class name and the database
table name, the
indentity-type specifies whether JDO controls
the id (primary key in the database) for
Employee. An identity type of
datastore instructs JDO to automatically create
and manage ids for persisted objects and an identity type of
application allows the developer to specify
keys (the approach used in this article).
Now let's consider the first field:
<field name="social" primary-key="true">
<column name="SOCIAL" jdbc-type="BIGINT"/>
</field>
The name attribute of the
field tag defines the name of the datamember
for the Employee Java object. The
primary-key attribute specifies that the
database column and thus the corresponding datamember for the Java object
are unique identifiers for Employee. The
name attribute of the
column tag defines the name of the column to be
mapped to the Employee datamember. The
jdbc-type specifies the JDBC datatype that
should be used to map the column datatype in the database.
Create a log4j.properties file
JPOX uses Apache log4J to report errors. This requres the
log4j.jar to be included in the classpath when
enhancing your classes (enhancing is covered in the next section) and
creating a log4J property file. Create a
log4j.properties file with the following
content and put it in the jdo-example/admin
directory:
Listing 4. log4j.properties
# Define the destination and format of our logging
log4j.appender.A1=org.apache.log4j.FileAppender
log4j.appender.A1.File=jpox.log
log4j.appender.A1.layout=org.apache.log4j.PatternLayout
log4j.appender.A1.layout.ConversionPattern=%d{HH:mm:ss,SSS} (%t) %-5p [%c] - %m%n
# JPOX Categories
log4j.category.JPOX.JDO=INFO, A1
log4j.category.JPOX.Cache=INFO, A1
log4j.category.JPOX.MetaData=INFO, A1
log4j.category.JPOX.General=INFO, A1
log4j.category.JPOX.Utility=INFO, A1
log4j.category.JPOX.Transaction=INFO, A1
log4j.category.JPOX.RDBMS=DEBUG, A1
log4j.category.JPOX.Enhancer=INFO, A1
log4j.category.JPOX.SchemaTool=INFO, A1
|
If you would like to customize the default property file or just learn more about how JPOX uses Apache Log4J, refer to http://www.jpox.org/docs/1_1/logging.html.
Several additional methods are needed to persist and retrieve the
Employee object to a database. The
Employee class must implement a special
interface, javax.jdo.spi.PersistenceCapable,
which has eight static fields and over twenty methods. Although this
interface could be implemented manually for every class you persist, in
practice the JPOX libraries are used to do this automatically by byte code
"enhancing" the class files of the objects to be persisted.
Use the following command in the /jdo-example
directory to enhance the Employee class:
> java -cp classes:jdo-2.0.jar:jpox.jar:bcel.jar:jpox-enhancer.jar:log4j.jar
-Dlog4j.configuration=file:admin/log4j.properties org.jpox.enhancer.JPOXEnhancer admin/package.jdo
> java -cp classes;jdo-2.0.jar;jpox.jar;bcel.jar;jpox-enhancer.jar;log4j.jar
Dlog4j.configuration=file:admin\log4j.properties org.jpox.enhancer.JPOXEnhancer admin\package.jdo
The command should result in the following:
JPOX Enhancer (version 1.1.0) : Enhancement of classes
ENHANCED: example.Employee
It is also important to note that there is now a
jpox.log file in
/jdo-example. Inspect this log if you encounter
any errors throughout this article.
Sample application that persists the Employee object
As an example of an application that uses the
Employee class, the following code creates an
instance of the Employee class, populates it,
and persists it to an IBM DB2 database. A key part in developing an
application that uses JDO is the
javax.jdo.PersistenceManager. This object
creates transactions and persists JDO objects. For more information on the
PersistenceManager, see the JDO 2.0 spec
available from:
http://jcp.org/aboutJava/communityprocess/pr/jsr243/index.html
This application creates an Employee instance, persists this single instance of Employee into DB2, and updates the empoyee's information. As a final task, it deletes the Employee from the database. Thus, it performs a Create, Update, and Delete (if you are familiar with the CRUD acronym). To better observe the behavior of the application, pauses are introduced which block application execution until the end user presses the ‘Enter’ key at the command line. This allows you to inspect the contents of the table in DB2 after each operation.
Instead of throwing the entire class at you immediately, we are going to
dissect it one method at a time. Let's start with the class’s datamembers
and main method. Create a MyApplication.java
class as follows and put it in the
/jdo-example/src/example directory.
Listing 5. MyApplication.java
package example;
import java.io.IOException;
import java.util.Collection;
import java.util.Properties;
import javax.jdo.JDOHelper;
import javax.jdo.PersistenceManager;
import javax.jdo.PersistenceManagerFactory;
import javax.jdo.Query;
import javax.jdo.Transaction;
public class MyApplication {
/* persistence related constants */
final static String
persistenceManagerFactoryClass = "org.jpox.PersistenceManagerFactoryImpl",
databaseDriverName = "com.ibm.db2.jcc.DB2Driver",
connectionURL = "jdbc:db2://localhost:50000/MYDB",
username = "db2admin",
password = "db2admin",
autoCreateSchema = "false",
validateTables = "false",
validateConstraints = "false";
/**
* Main method performs Create, Update, and Delete of Employee
* Pauses are inserted inbetween so the database can be observed after each step
*/
public static void main(String[] args) {
PersistenceManager pm = createPersistenceManager();
createEmployee(pm);
pause("Employee created, waiting for user input:");
updateEmployee(pm);
pause("Employee updated, waiting for user input:");
deleteEmployee(pm);
System.out.println("Employee deleted.");
pm.close();
}
/* ADD SUBSEQUENT WORKER METHODS HERE */
}
|
As you can see, the class begins with String
constants. These are all the parameters JDO needs to persist data. Many of
them should look familiar as they are required to connect to DB2 using the
type 4 JDBC driver. If this is unfamiliar to you, see the
Resources section of this article for more
information. Notice the assumption of db2admin
as both the DB2 username and password.
The last three strings—autoCreateSchema,
validateTables,
validateConstraints—are specific to JPOX and
are covered in the next bit of code that actually creates the
PersistenceManager.
The main method simply delegates each operation
to worker methods. Each worker method creates its own transaction and
handles any error checking.
The createPersistenceManager() worker method
Let’s look at the first worker method which creates the
PersistenceManager. Append the
MyApplication.java class with the following
method:
Listing 6. createPersistenceManager()
/**
* Creates a PersistenceManager to create transactions
* and to persist JDO persistence compatible objects
*/
private static PersistenceManager createPersistenceManager()
{
Properties properties = new Properties();
properties.setProperty("javax.jdo.PersistenceManagerFactoryClass",
persistenceManagerFactoryClass);
properties.setProperty("javax.jdo.option.ConnectionDriverName",
databaseDriverName);
properties.setProperty("javax.jdo.option.ConnectionURL",connectionURL);
properties.setProperty("javax.jdo.option.ConnectionUserName",username);
properties.setProperty("javax.jdo.option.ConnectionPassword",password);
properties.setProperty("org.jpox.autoCreateSchema",autoCreateSchema);
properties.setProperty("org.jpox.validateTables",validateTables);
properties.setProperty("org.jpox.validateConstraints",
validateConstraints);
PersistenceManagerFactory pmf =
JDOHelper.getPersistenceManagerFactory(properties);
return pmf.getPersistenceManager();
}
|
Notice that a factory pattern which takes in all necessary properties is
used to create a
javax.jdo.PersistenceManagerFactory. A
PersistenceManager is then created from the
factory. Both the creation of the factory and the manager object incur
high performance costs (similar to connecting to a database with JDBC).
Thus, they should be created only once if possible.
Consider the following definitions for the JPOX related properties:
- org.jpox.autoCreateSchema: This property specifies whether you
want JPOX to create the database schema to persist the JDO objects or
whether it should use an existing schema. In this article we are using
an existing schema so "false" is selected.
- org.jpox.validateTables: This property specifies whether to
validate tables against the persistence definition. If this is set to
true, JPOX validates the database tables according to the JDO metadata
(in our case
package.jdois the name of the metadata file) which incurs a small performance penalty. In this article, this feature is turned off due to the simplicity of theEmployeeobject. - org.jpox.validateConstraints: Similar to
validateTables, but validation occurs at the column level. Again this feature is disabled with a parameter of "false."
The createEmployee() worker method
The next worker method persists Employee with
some hard-coded data:
Listing 7. createEmployee()
/**
* Worker method that persists the Employee
*/
private static void createEmployee(PersistenceManager pm) {
Employee emp = new Employee(556974421L, "John", "Doe", 2090.12F);
Transaction txn = pm.currentTransaction();
txn.begin();
pm.makePersistent(emp);
txn.commit();
}
|
This is where you can start to see how JDO allows you to handle persistence
in an object-oriented fashion. Persisting an object is as simple as
calling the makePersistent() method of the
PersistenceManager. Also notice that no
exception handling is required. JPOX by default rolls back transactions
that fail and represents this in its logs. If you need more complex
behavior, you can add try-catch-finally blocks.
The updateEmployee() worker method
The update worker method persists changes to the state of
Employee:
Listing 8. updateEmployee()
/**
* Worker method to update the first and last name of the employee with
*/
private static void updateEmployee(PersistenceManager pm) {
Transaction txn = pm.currentTransaction();
txn.begin();
Query query = pm.newQuery
("SELECT FROM example.Employee WHERE social == 556974421");
query.setUnique(true);
Employee emp = (Employee) query.execute();
emp.setFirstName("Samuel");
emp.setLastName("Maximus");
txn.commit();
}
|
The first step in updating employee John Doe's information is to find his
instance in the datastore. This is done by forming a JDOQL query. JDOQL is
a query language for Java Data Objects which has SQL-like syntax. Notice
that the unique attribute of the Query is set
with setUnique(true). This specifies that the
query only returns one result. This way when the query is executed, it
returns an instance of Employee instead of a
java.util.Collection.
Once we have the instance of Employee we simply
modify its data with traditional setters. Notice that there is no need to
persist the Employee object again after the
update with setter method. JDO intercepts updates to persisted objects and
automatically persists any changes in state. However, the changes are not
made permanent until the commit() method is
called.
The deleteEmployee() worker method
The following method deletes an instance of
Employee:
Listing 9. deleteEmployee()
/**
* Delete all persisted instance of Employee with social of 556974421
*/
private static void deleteEmployee(PersistenceManager pm) {
Transaction txn = pm.currentTransaction();
txn.begin();
Query query = pm.newQuery
("SELECT FROM example.Employee WHERE social == 556974421");
Collection result = (Collection) query.execute();
pm.deletePersistentAll(result);
txn.commit();
}
|
Again, in order to delete an instance of
Employee, we must form a query. In this case,
the setUnique() method is not used so the query
returns a java.util.Collection
(setUnique() could be used as well; we did
things different this time to show JDO's use of collections for query
results). Once the collection is returned from the query, all persisted
objects inside the collection (in our case there is only one) are deleted
using the deletePersistentAll() method call to
the PersistenceManager.
Development of pause() worker method
The following method prints text to the command line and waits for user input between the creation, update, and deletion of the employee's data:
Listing 10. pause()
/**
* Method which blocks until the user enters a character at the command line
*/
private static void pause(String msg){
System.out.println(msg);
try {
System.in.read();
System.in.skip(System.in.available());
}
catch( IOException ioe ) {
throw new RuntimeException(ioe);
}
}
|
As stated earlier, pauses are placed throughout the code for you to be able to check how the database is affected by the application's code.
Compile and run MyApplication.java
Before running the code, be sure that DB2 is started and configured to
accept the user specified in the code above (in this example,
db2admin). Use the following commands to
compile the application from the /jdo-example
directory:
> javac -classpath classes:jdo-2.0.jar:jpox.jar -d classes src/example/MyApplication.java
> javac -classpath classes;jdo-2.0.jar;jpox.jar -d classes src\example\MyApplication.java
Use the following commands to run the application:
> java -cp classes:jdo-2.0.jar:jpox.jar:log4j.jar:db2jcc.jar:db2jcc_license_cu.jar:admin
-Dlog4j.configuration=file:admin/log4j.properties example.MyApplication
> java -cp classes;jdo-2.0.jar;jpox.jar;log4j.jar;db2jcc.jar;db2jcc_license_cu.jar;admin
-Dlog4j.configuration=file:admin\log4j.properties example.MyApplication
As stated earlier, it is recommended that you inspect the database between CRUD operations. You can use the following DB2 command line processor command to ensure the DB2 data is being manipulated as expected:
db2 => select * from EMPLOYEE
When things go wrong… analyzing the JPOX logs
JPOX uses Log4J as its logging mechanism. As mentioned earlier, be sure to
inspect jpox.log in the
/jdo-example directory if you encounter
problems. JPOX also has excellent forums which provide a wealth of
information and are highly active in the event that you get stuck (see
Resources).
Using JPOX in a J2EE Application Server
The use of JPOX is supported in a J2EE Application Server setting such as that of IBM WebSphere Application Server V6. JPOX provides a connector facility which one uses in a J2EE setup. Use of JPOX with IBM WebSphere Application Server V6 is beyond the scope of this article. However, the topic will be covered in an upcoming sequel to this article.
This introductory article should be enough to get you started with the JPOX implementation of JDO 2.0. The popularity of JPOX stems from its easy to use syntax that abstracts a Java programmer from the intricacies of database programming. JPOX has been chosen by Sun to be the reference implementation for the JDO 2.0 specification. Since JPOX is an implementation of the JDO standard API, you can switch to another JDO implementation for object persistence without too much labor if you decide JPOX does not fit your needs.
- Sun JDO 2.0 specification.
http://www.jcp.org/en/jsr/detail?id=243
- JDO 2.0 API javadocs.
http://www.jpox.org/docs/1_1/javadocs/JDO2/index.html
- JPOX 1.1 documentation.
http://www.jpox.org/docs/1_1/index.html
- JPOX forums.
http://www.jpox.org/servlet/forum/index
- JPOX 1.1 tutorial.
http://www.jpox.org/docs/1_1/tutorials/tutorial.html
- DB2 JDBC driver information.
Overview of Java Development in DB2 UDB for Linux, UNIX, and Windows

Robert R. Peterson is part of the enablement team under IBM Software Services for WebSphere. He works to ensure that the WebSphere portfolio of products brings IBM's clients the greatest value possible. Robert is an accomplished inventor and co-author of WebSphere Application Server V6: Performance and Scalability. He is an alumni of IBM's prestigious Extreme Blue Program and holds a M.S. in Computer Engineering from the University of Florida.





