Articles Index
The task of persisting Java technology objects to any relational
database is challenging because it involves serializing the
hierarchically structured Java objects to a tabular-structured database
and vice versa. This challenge is unique because of the need to map
Java technology objects to database columns and records in a way that
is optimized for both speed and efficiency. The Java Data Objects (JDO)
specification, Java Specification Request (JSR) 12,
defines an API for a standard way to transparently persist plain Java
technology object and database access. Using JDO, Java application
developers can write code to access the underlying data store without
using any database-specific code. JDO is designed to work in multiple
tiers of an enterprise architecture including the Java Platform,
Standard Edition (Java SE, formerly known as J2SE), web tier, and
application servers.
This article provides a tutorial on JDO. It covers the essentials of
JDO and offers sample code to give a flavor of the effort involved in
using JDO to persist your Java technology objects.
Introduction
Persistence, the long-term storage of information after program
exit, can be achieved using different options in the Java language. The
simplest is file I/O, but this option is not suitable for
enterprise-level storage (for obvious reasons). Other options include
the following:
- Serialization. Simple API with no support for query, sharing
among multiple users, transactions, or partial read or update. In
addition, it is not suitable for large-capacity data storage.
- Java DataBase Connectivity (JDBC) software. Full access to
SQL database functionality. JDBC requires the developer to explicitly
manage the values of fields and to map them into relational database
tables. In this case, the developer would need to know another language
(for example, a query language such as SQL).
- Enterprise JavaBeans (EJB) architecture Container Managed Persistence (CMP).
As part of the Java Platform, Enterprise Edition (Java EE, formerly
known as J2EE) component model, CMP provides a portable object
persistence service to EJB containers. However, it is not a general
purpose persistence facility for the Java platform.
A viable alternative is the JDO API, which provides a standard approach
for achieving object persistence in Java technology by using a
combination of XML metadata and bytecode enhancement to ease the
development complexity and overhead.
The JDO Architecture
The high-level JDO API is designed to provide a transparent interface
for developers to store data, without having to learn a new data access
language (such as SQL) for each type of persistent data storage. JDO
can be implemented using a low-level API (such as JDBC) to store data.
It enables developers to write Java code that transparently accesses
the underlying data store, without using database-specific code. JDO
was developed as a JSR in the Java Community Process (JCP) program: JDO
1.0, in existence since 2002, is JSR 12; and JDO 2.0, approved in early 2005 and under development, is JSR 243.
JDO 2.0 is a rich and full-featured JSR specification for Plain Old
Java Objects (POJOs) persistence, and multiple vendors are providing
competing implementations. In addition, many vendors will likely
implement JDO 2.0 and EJB 3.0 in the same product. This will allow you
to use both APIs at the same time, giving you an easier way to
gradually migrate to EJB 3.0 POJO, which will become the persistence
model of choice.
The two main objectives of the JDO architecture, which is shown in
Figure 1, are to provide Java application developers a transparent Java
technology-centric view of persistent information and to enable
pluggable implementations of data stores into application servers.
 |
Figure 1: JDO Architecture Click here for a larger image.
|
|
It is important to note that JDO does not define the type of data
store: You can use the same interface to persist your Java technology
objects to a relational database, an object database, XML, or any data
store.
The benefits of using JDO are the following:
- Portability. Applications written using the JDO API can be
run on multiple implementations available from different vendors
without changing a single line of code or even recompiling.
- Transparent database access. Application developers write code to access the underlying data store without any database-specific code.
- Ease of use. The JDO API allows application developers to
focus on their domain object model (DOM) and leave the details of the
persistence to the JDO implementation.
- High performance. Java application developers do not need to
worry about performance optimization for data access because this task
is delegated to JDO implementations that can improve data access
patterns for best performance.
- Integration with EJB. Applications can take advantage of EJB
features such as remote message processing, automatic distributed
transaction coordination, and security using the same DOMs throughout
the enterprise.
Use of JDO vs. JDBC and EJB
JDO is not meant to replace JDBC. They are complementary approaches
with unique strengths, and developers with different skill sets and
different development objectives can use either. For example, JDBC
offers developers greater flexibility by giving them direct control
over database access and cache management. JDBC is a more mature
technology with wide industry acceptance. JDO, on the other hand,
offers developers convenience by hiding SQL. This frees Java platform
developers to focus on the DOM without necessarily knowing or having to
learn SQL, while JDO takes care of the details of the field-by-field
storage of objects in a persistent store.
JDO is designed to complement EJB. EJB CMP provides portable
persistence for containers, and JDO can be integrated with EJB in two
ways: (1) through Session Beans with JDO persistence-capable classes to
implement dependent objects (persistent helper classes for Session
Beans) and (2) through Entity Beans with JDO persistence-capable
classes used as delegates for both Bean Managed Persistence (BMP) and
Container Managed Persistence (CMP).
You can learn more about the relationship between JDO and JDBC, as well as about the relationship between EJB 2.0 CMP and JDO.
The Road to POJO
The significant differences in persistence models for JDO and EJB
has caused some confusion among developers. In response, Sun
Microsystems is leading a community effort to create a single POJO
persistence model for the Java technology community. This effort is
realized under the auspices of JSR 220,
led by Linda DeMichiel. Members of the JDO 2.0 Expert Group (JSR 243)
were invited to join the EJB 3.0 (JSR 220) Expert Group.
The objective is to create a POJO persistence model that provides a
single object-relational mapping facility for all Java application
developers who work with Java SE and Java EE platforms. It is worth
noting that Oracle is joining Sun as the co-specification lead for EJB 3.0. The EJB 3.0 public review draft is now available.
JSR 243 (JDO 2.0) follows the directions outlined in the letter to the Java technology community from the specification leads for JSRs 220 and 243.
JDO 2.0 does not propose specific API convergence with EJB 3.0
persistence but rather an evolution of JDO 1.0.2. But the similarities
between the POJO persistence model of JDO and EJB 3.0 will enable JDO
customers to easily embrace the new EJB 3.0 persistence model while
meeting immediate needs with JDO 2.0. In addition, JSR 243 intends
JDOQL to be used as an alternative query language with EJB 3.0
persistence. The language has been updated to work better with EJB 3.0.
To learn more about the single persistence model, please see the EJB/JDO Persistence FAQ.
JDO Class Types
There are three types of classes in JDO:
- Persistence-capable. This category represents classes whose
instances can be persisted to a data store. Note that these classes
need to be enhanced according to a JDO metadata specification before
they are used in a JDO environment.
- Persistence-aware. These classes manipulate persistence-capable classes. The
JDOHelper
class provides methods that allow interrogation of the persistent state
of an instance of a persistence-capable class. Note that these classes
are enhanced with minimal JDO metadata.
- Normal. These classes are not persistable and have no knowledge of persistence. They require no JDO metadata.
Life Cycle of JDO Instances
JDO manages the life cycle of an object from creation to deletion.
During its life, a JDO instance transitions among various states until
it is finally garbage collected by the Java Virtual Machine (JVM). The
transition between states is achieved using methods of the PersistenceManager class including the TransactionManager -- such as makePersistent() , makeTransient() , deletePersistent() -- and committing or rolling back changes that such operations make.
Table 1 shows the 10 states defined by the JDO specification. The first
seven states are required, and the last three are optional. If an
implementation does not support certain operations, then the three
optional states are not reachable.
Table 1: The JDO Life-Cycle States
State |
Description |
Transient |
Any object created using a developer-written constructor that
does not involve the persistence environment. No JDO
identity is associated with a transient instance. |
Persistent-new |
Any object that has been requested by the application component
to become persistent using the makePersistent() method
of the PersistenceManager class. Such an object will
have an assigned JDO identity. |
Persistent-dirty |
Any persistent object that was changed in the current transaction. |
Hollow |
Any persistent object that represents specific data in the
data store but whose values are not in the instance. |
Persistent-clean |
Any persistent object that represents specific transactional
data in the data store and whose values have not been changed in the
current transaction. |
Persistent-deleted |
Any persistent object that represents specific data in the
data store and that has been deleted in the current
transaction. |
Persistent-new-deleted |
Any persistent object that has been made newly persistent and
deleted in the same transaction. |
Persistent-nontransactional |
Any persistent object that represents data in the data store,
whose values are currently loaded but not transactionally
consistent |
Transient-client |
Any persistent object that represents a transient transactional
instance whose values have not been changed in the current
transaction. |
Transient-dirty |
Any persistent object that represents a transient transaction
instance whose values have been changed in the current
transaction. |
Figure 2 depicts the state transitions of JDO instances.
 |
Figure 2: State Transitions of JDO Instances Click here for a larger image.
|
|
The snippets of code later in this article demonstrate how to perform some of the operations we have just discussed.
The JDO Reference Implementation
The JDO reference implementation, which is available from Sun Microsystems, comes with a file-based storage mechanism called fstore . Note that Sun Microsystems has donated JDO to the open source community. JDO 1.0 and 2.0 will be developed as part of the Apache JDO project. But due to timing constraints, the JDO 2.0 reference implementation is being built not as an Apache project but as a JPOX release. Several commercial implementations are available.
The JDO Programming Model
JDO defines two types of interfaces: the JDO API (in the javax.jdo package) and the JDO service provider interface (SPI) (in the javax.jdo.spi package). The JDO API is for application developers, and the JDO SPI is for container providers and JDO vendors.
An application has two primary interfaces to JDO:
PersistenceManagerFactory represents the point of access that application developers use to obtain an instance of PersistenceManager . Instances of this interface can be configured and serialized for later use. However, note that once the first PersistenceManager is obtained from the PersistenceManagerFactory , the factory can no longer be configured. You can use the following code to obtain a PersistenceManagerFactory :
// set some properties for the JDO implementation and data store
Properties props = new Properties();
props.put(...);
// get a PersistenceManagerFactory
PersistenceManagerFactory pmf = JDOHelper.getPersistenceManagerFactory(props);
|
PersistenceManager is the primary interface for
JDO-aware application components. It provides methods to make an object
persistent, as well as retrieving persistent objects and removing them
from persistent storage. You can use the following code to obtain a PersistenceManager :
PersistenceManager pm = pmf.getPersistenceManager();
|
Once a PersistenceManager is obtained, an application
can perform tasks such as making an object persistent, retrieving an
object from persistence, deleting an object from persistence, updating
an object, and so on.
The following snippet of code shows how to make an object persistent. It updates the state of the object from Transient to Hollow :
Employee emp = new Employee("Sarah Jones", 23, 37000.00);
Transaction tx;
try {
tx = pm.currentTransaction();
tx.begin();
pm.makePersistent(emp);
tx.commit();
} catch (Exception e) {
if(tx.isActive()) {
tx.rollback();
}
}
|
Retrieving an object from persistence is equally simple: You can use Extent (a holder for information) or Query (which provides more accurate filtering). Here is an example using Extent :
try {
tx = pm.currentTransaction();
tx.begin();
Extend ex = pm.getExtent(Employee.class, true);
Iterator i = ex.iterator();
while(i.hasNext()) {
Employee obj = (Employee) i.next();
}
tx.commit();
} catch (Exception e) {
if(tx.isActive()) {
tx.rollback();
}
}
|
Finally, deleting an object from persistence can be done simply by
first retrieving the object from persistence as shown earlier and then
invoking the deletePersistent(obj) method.
Querying Objects
The JDO specification requires that vendors provide a query capability
using JDOQL, which is a query language oriented around the objects that
are persisted. The PersistenceManager class defines methods for constructing instances of Query -instance implementation classes. A query filter is specified as a boolean expression that resembles SQL's boolean operators.
The Development Life Cycle: Using JDO in Your Applications
Building a JDO application consists of six simple steps:
- Design your domain classes as you would normally do. The only requirement for a class that requires persistence is that it has a default constructor, which can be
private .
- Define the persistence definition using metadata. In this
step, you write the metadata, specifying which classes and fields
should be persisted and so on. The file can contain persistence
information for a single class or for one or more packages that have
persistent classes.
The name of the metadata file for one class is the name of the class followed by the .jdo suffix. Note that this file must be placed in the same directory as the .class files. The metadata file for an entire package must be contained in a file named package.jdo . The metadata file can be developed using XDoclet or manually. Here is a sample metadata file for two classes:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE jdo SYSTEM "jdo.dtd">
<jdo>
<package name="com.xyz.hr">
<class name="Employee" identity-type="application" objectidclass="EmployeeKey">
<field name="name" primary-key="true">
<extension vendor-name="sunw" key="index" value="btree"/>
</field>
<field name="salary" default-fetch-group="true"/>
<field name="dept">
<extension vendor-name="sunw" key="inverse" value="emps"/>
</field>
<field name="boss"/>
</class>
<class name="Department" identity-type="application" objectidclass="DepartmentKey">
<field name="name" primary-key="true"/>
<field name="emps">
<collection element-type="Employee">
<extension vendor-name="sunw" key="element-inverse" value="dept"/>
</collection>
</field>
</class>
</package>
</jdo>
|
- Compile the classes and instrument them using a JDO enhancer.
Any persistence-capable class must be enhanced before its instances can
be managed by a JDO persistence engine. The JDO bytecode enhancer
transforms the class by making specific changes to the class definition
to enable the state of any persistent instances to be synchronized with
the representation of the data in the data store. The JDO enhancer that
comes with the reference implementation available from Sun Microsystems
can be run as follows:
prompt> java -classpath
%JDO-HOME%\lib\jdo.jar;%JDO-HOME%\lib\jdori.jar;
%JDO-HOME%\jdori-enhancer.jar com.sun.jdori.enhancer.Main -d
\enhanced -s . -f path\tp\package.jdo path\to\theclasses.class
|
Note that the main arguments to the JDO enhancer are the name of a .jdo file and the name of the .class file. In addition,
- The
-d option specifies the destination directory for the output files.
- The
-s option specifies the source path for jdo and classfiles.
- The
-f option to forces overwriting output files.
If you omit this step, then the exception ClassNotPersistenceCapableException will be thrown when you try to run your application and persist an object.
- Generate the database tables where classes are to be persisted.
This step is optional if you already have an existing database schema.
Basically, you must generate the required tables, indexes, and foreign
keys for the classes defined in the JDO metadata file. Some JDO
implementations come with a schema tool that can be used to
automatically generate all of this based on the JDO metadata file.
- Develop the code to persist your objects. In this step, you
define which classes are actually persisted and when. As mentioned
earlier, the initial step is to obtain access to a
PersistenceManager .
- Run your application. Use the
java command and include the necessary .jar files in your classpath.
Conclusion
JDO provides for interface-based definitions of data stores and
transactions and for selection and transformation of persistent storage
data into native Java technology objects. It addresses the need for
three things: (1) a standard for storing Java technology objects
persistently in transactional data stores, (2) a standard way to treat
relational database data as Java technology objects, and (3) a standard
way to define transactional semantics associated with those objects.
The JDO API, consisting of just a few interfaces, is simple to learn
and use, but more importantly it defines a standard for object
persistence. There are many JDO implementations to choose from, some of
them free. JDO gains its simplicity by allowing you to work with POJOs
rather than proprietary APIs.
JDO has a vibrant community. So if you are looking for a persistence
solution for your POJOs, JDO is a standard developed through the JCP
program. JDO 2.0 provides a rich and full-featured JSR specification
for POJO persistence, and multiple vendors are providing competing
implementations. EJB 3.0, which Sun Microsystems is creating with
experts from many different persistence vendors, will be the
persistence model of choice for the future.
For More Information
Acknowledgments
Special thanks to Mimi Hills of Sun Microsystems, whose feedback helped me improve this article.
|