Getting Started With Java Data Objects (JDO): A Standard Mechanism for Persisting Plain Java Technology Objects

   
By Qusay H. Mahmoud, August 9, 2005  

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

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 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

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();
   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.

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:

  1. 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.
  2. 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.
  3. 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>
    
     
  4. 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.
  5. 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.
  6. 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.
  7. 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.

Rate and Review
Tell us what you think of the content of this page.
Excellent   Good   Fair   Poor  
Comments:
Your email address (no reply is possible without an address):
Sun Privacy Policy

Note: We are not able to respond to all submitted comments.