By Qusay H. Mahmoud,
August 9, 2005
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.
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:
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 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.
Click here for a larger image.
Figure 1: JDO Architecture
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:
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 EJB 2.0 CMP and JDO.
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.
There are three types of classes in JDO:
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. 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
The snippets of code later in this article demonstrate how to perform some of the operations we have just discussed.
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.
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();
Extent 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.
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.
Building a JDO application consists of six simple steps:
private
. 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>
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,
-d
option specifies the destination directory for the output files. -s
option specifies the source path for jdo and classfiles. -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.
PersistenceManager
. java
command and include the necessary .jar
files in your classpath.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.
Acknowledgments
Special thanks to Mimi Hills of Sun Microsystems, whose feedback helped me improve this article.