An introduction to JDO 2.0 using JPOX and DB2 Universal Database

The Java™ Data Objects (JDO) specification introduced by the Java Community Process (JCP) is a framework which persists plain Java objects to a data store such as an RDBMS. Java Persistent Objects (JPOX) is a JDO 1.0 (JSR 012) and JDO 2.0 (JSR 243) compliant implementation. This article shows how to use the JPOX implementation to abstract the tedious writing of SQL to persist and retrieve data from your DB2® Universal Database™ (DB2 UDB) database. We showcase JPOX 1.1, which has been chosen by the Java Community Process to be the reference implementation of JDO 2.0. JPOX is released under the Apache 2.0 open source license and is free to use.

Robert Peterson (rrpeters@us.ibm.com), WebSphere Enablement, Austin, TX, EMC

Author photoRobert 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.


developerWorks Contributing author
        level

Kulvir Singh Bhogal (kbhogal@us.ibm.com), Software Services for WebSphere, Fort Worth, TX, EMC

Kulvir Bhogal photoKulvir Singh Bhogal works as an IBM consultant, devising and implementing Java-centric solutions at customer sites across the nation.



23 June 2005

The business case for JDO 2.0

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.

Prerequisites

You'll need the following software in order to try out the implementation described in this article:

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. Download jdo-2.0.jar from 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/lib directory 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.jar and db2jcc_license_cu.jar are 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
Employee 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:

Linux or UNIX

     /opt/IBM/db2/V8.x/bin/db2

Windows

     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 Employee.java

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


Enhance Employee.class

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:

Linux or UNIX

> 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

Windows

> 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.jdo is 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 the Employee object.
  • 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:

Linux or UNIX

     > javac -classpath classes:jdo-2.0.jar:jpox.jar -d classes src/example/MyApplication.java

Windows

     > javac -classpath classes;jdo-2.0.jar;jpox.jar -d classes src\example\MyApplication.java

Use the following commands to run the application:

Linux or UNIX

     > 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

Windows

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


Conclusion

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.

Resources

Comments

developerWorks: Sign in

Required fields are indicated with an asterisk (*).


Need an IBM ID?
Forgot your IBM ID?


Forgot your password?
Change your password

By clicking Submit, you agree to the developerWorks terms of use.

 


The first time you sign into developerWorks, a profile is created for you. Information in your profile (your name, country/region, and company name) is displayed to the public and will accompany any content you post, unless you opt to hide your company name. You may update your IBM account at any time.

All information submitted is secure.

Choose your display name



The first time you sign in to developerWorks, a profile is created for you, so you need to choose a display name. Your display name accompanies the content you post on developerWorks.

Please choose a display name between 3-31 characters. Your display name must be unique in the developerWorks community and should not be your email address for privacy reasons.

Required fields are indicated with an asterisk (*).

(Must be between 3 – 31 characters.)

By clicking Submit, you agree to the developerWorks terms of use.

 


All information submitted is secure.

Dig deeper into Information management on developerWorks


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=1
Zone=Information Management, Java technology
ArticleID=86656
ArticleTitle=An introduction to JDO 2.0 using JPOX and DB2 Universal Database
publish-date=06232005