Getting Started with Java Data Objects
Java Data Objects, or JDO, is a persistence framework that lets developers keep Java objects in a database without writing SQL. It is built on top of the Java Persistence API but offers its own lightweight approach that is independent of a specific database vendor. If you have used JDBC, you know that it forces you to manage result sets, statement objects, and SQL strings. With JDO you simply persist, query, and delete your objects, while the JDO implementation handles the conversion to database rows.
Unlike serialization, which writes a binary representation of an object to a file or stream, JDO stores object state in a relational database and supports multi‑user concurrency. JDO’s transaction support aligns with the Java Transaction API (JTA) so that updates can be committed or rolled back just as you would expect from any transactional system. That means you can treat a database as a natural extension of your object model, keeping the code that manipulates business data in the same domain classes you already use.
When the JDO standard landed, several commercial and open source products emerged to provide a reference implementation. CocoBase, WebGain TOPLink, and Castor JDO were early options, each offering its own configuration and runtime. OpenFusion JDO, from Prism Technologies, was chosen for this guide because it has a straightforward build process and includes the JDO Enhancer command line tool that injects persistence capabilities into your bytecode. The Enhancer is a key part of the workflow, as it transforms plain Java classes into objects that the persistence engine can manage.
By the time you finish this walkthrough you will have a small “Address Book” application that creates, reads, updates, and deletes Person records in a database - all without writing any SQL. The process is largely the same for other JDO implementations: you write your domain classes, define a descriptor XML, run the Enhancer, and then use the PersistenceManager API to interact with the datastore. The examples here rely on the OpenFusion JDO JARs and the accompanying DBHelper utility, but the overall structure is portable.
In the next section you’ll see how to define the Person class. The class follows normal JavaBean conventions: private fields with public getters and setters. JDO does not require the class to implement a special interface or extend a base class; it simply inspects the public fields or accessor methods and persists the supported types. When you run the Enhancer, it adds a small proxy layer that tracks dirty fields and manages the persistence lifecycle. This approach keeps your business logic untouched and lets you focus on the data you care about.
The descriptor file is the bridge between your Java classes and the database schema. It tells the Enhancer which classes are persistent, which fields map to columns, and any special configuration such as identity generation strategy. In the OpenFusion example the descriptor is tiny, but you can add more complex mappings later if you need to handle composite keys or inheritance hierarchies. Once you understand the basics, extending the descriptor is a matter of adding a few more lines, not rewriting the code.
Defining the Persistent Domain Class
The Person class is the heart of our sample. It contains fields for an identifier, name, address, and email, each with a standard getter and setter. Because JDO works with JavaBeans, each property must either be a public field or have a public accessor pair. In our example the fields are private and accessed through the getters and setters, which is the usual practice for encapsulation. JDO reads and writes these properties through reflection, so the implementation details are hidden from the developer.
One of the nice aspects of JDO is that you do not need to add any special annotations or extend a base class to mark the class as persistent. The only requirement is that the type of each field is supported by JDO. Common types such as String, Integer, Long, Date, and List are automatically handled. If you try to persist a type that JDO does not support - like a Thread or a Socket - you will get a runtime error during enhancement. For complex types you can write a custom serializer, but that goes beyond the scope of this guide.
Another consideration is the primary key. In JDO you can choose a datastore identity, meaning the database assigns the primary key, or application identity, where the application supplies the key. In the sample we use datastore identity, so the JDO provider will create an auto‑increment column for the Person class. The Enhancer will add a field named jdoStateManager to manage the persistence state, and a method setId that is called by the provider when the object is persisted. This field and method are invisible to your code; you simply call getId after persisting if you need to know the assigned key.
The Person class also includes a constructor that initializes all properties, making it easy to create sample data in code. The constructor accepts parameters in the same order as the fields, and sets each property using the setter methods. This style keeps the constructor simple and encourages the use of the accessor methods throughout the application.
When you compile the Person class, it is still a plain Java object. The Enhancer will later inject persistence behavior, but until that point it behaves like any other POJO. The process of adding JDO support is therefore non‑intrusive: you write your domain objects once, and they can be persisted by the JDO provider after enhancement.
Because the JDO provider needs to know where to store the data, the descriptor file will reference the database connection settings. In the OpenFusion example, a small jdo.properties file contains the JDBC URL, driver class, username, and password. The DBHelper utility reads this file and builds a connection pool behind the scenes. This means that the persistence code does not need to specify the database details at runtime; they are supplied through the descriptor.
Setting Up Persistence with the JDO Enhancer
The JDO Enhancer is a command‑line tool that takes compiled classes and a descriptor XML and injects the necessary persistence logic. For OpenFusion JDO the enhancer is invoked as a Java jar with the following syntax:
java -classpath <enhancer-jar>;*;. org.openfusion.jdo.tools.JDOEnhancer -d <output-dir> -src <class-dir> <descriptor.xml>
In this example the descriptor is Person.jdo and the compiled classes live in bin. The enhancer writes the enhanced classes into enhanced. Once enhancement is finished, the rest of the application uses the enhanced classes as if they were ordinary objects, but the persistence framework can now track changes, manage identity, and interact with the database.
The descriptor file itself is a simple XML document. For our Person class it looks like this:
<jdo:class name="Person" <namespace> <identity>Datastore</identity> <table>PERSON</table> </jdo:class>
The namespace declaration is omitted for brevity but must match the JDO namespace. The identity element tells the provider to generate the primary key automatically. The table element maps the class to a database table named PERSON. If you need to specify column names that differ from the field names, you can add field elements inside the class definition with a column attribute.
OpenFusion JDO also allows the descriptor to include database creation scripts. When you run the enhancer with the -db flag it generates DDL statements that create the required tables. The output is a set of SQL files - one for each database platform you target. For Oracle the generated file is typically load_all.sql, which you can run in SQL*Plus or any other SQL client to set up the database schema before launching the application.
While the Enhancer adds persistence capabilities to the class bytecode, it also updates the descriptor to record which fields are persistent. This meta‑information is used by the PersistenceManager at runtime to map object properties to columns and to track changes. Because the Enhancer runs after compilation, the source code never changes; the process is transparent to the developer.
When you are ready to test the system, you will need to compile the code, run the Enhancer, apply the database scripts, and then start the Java application. Each of these steps is described in detail later, but the key point is that the Enhancer is a one‑time step per class change. After that, the persistence layer behaves like a normal JDO runtime, using the PersistenceManager API to perform operations on persistent objects.
Performing CRUD Operations with JDO
The PersistenceManager is the primary interface you use to interact with the JDO datastore. It is analogous to a JDBC Connection but works with objects instead of SQL statements. The typical workflow is: acquire a PersistenceManager, begin a transaction, perform operations, and then commit or rollback.
In our sample the PersonPersist class encapsulates this workflow. Its constructor creates a PersistenceManagerFactory using the descriptor and the DBHelper class, which loads the JDBC settings from jdo.properties. The factory is used to obtain a PersistenceManager whenever the application needs to touch the database.
Creating three Person records is straightforward. The persistPeople method instantiates three Person objects, each initialized with distinct names and addresses. Inside a transaction, it calls pm.makePersistentAll() with the array of objects. This method tells the PersistenceManager to store each object in the database. After the transaction commits, the objects acquire their datastore identities. The method returns an array of IDs that can be reused later for retrieval.
Reading the persisted objects back is equally simple. The showPeople method iterates over the stored IDs, retrieves each object using pm.getObjectById(), and prints its properties. Because the objects are fully populated, you can access any getter method directly. This demonstrates that the persistence layer restores the complete state of the object graph.
Updating a record involves loading the object, modifying a field, and committing the transaction. In the sample changeName method fetches the Person with a specific ID, calls setName() to modify the name, and then commits. The PersistenceManager tracks the dirty state of the object, so only the changed column is written back to the database. This selective update is one of the benefits of using an ORM framework: you do not have to write an UPDATE statement manually.
Deletion follows the same pattern. The deletePerson method obtains the Person object, calls pm.deletePersistent(), and commits. The Enhancer-generated code ensures that the deletion cascades properly if there are relationships, but in this simple example there are none.
Because all these operations are wrapped in transactions, the application behaves correctly in multi‑user scenarios. If two users try to modify the same Person record concurrently, JDO will enforce isolation levels defined by the underlying database. In most cases, optimistic locking is used, meaning that a version field is automatically incremented on each update. If a conflict is detected, the transaction will be rolled back and an exception thrown.
The main advantage of this approach is that you do not need to write any SQL. The PersistenceManager abstracts the database operations behind simple Java methods. You can focus on the business logic - creating, reading, updating, deleting - and let JDO translate that into the appropriate SQL statements. This reduces boilerplate code and lowers the chance of mistakes such as SQL injection vulnerabilities.
When you run the application, you will see the following sequence in the console: three people persisted, the list printed, a name change applied, and a deletion executed. Each step is logged through System.out.println statements that demonstrate the state before and after the operation.
Compiling, Enhancing, and Running the Sample
After you have written the Person, PersonPersist, and descriptor files, the build process begins with a simple compilation. On Windows you can use a command like:
javac -classpath ".;lib/" -d bin src/.java
On Unix or macOS replace the semicolon with a colon. This compiles all Java source files into the bin directory, keeping the classpath intact so that any required JDO JARs are available.
Next, run the JDO Enhancer. The command is:
java -classpath "lib/*;bin" org.openfusion.jdo.tools.JDOEnhancer -d enhanced -src bin Person.jdo
This tells the enhancer to read classes from bin, apply the descriptor Person.jdo, and output enhanced classes into the enhanced folder. The enhancer will also generate the SQL script needed to create the PERSON table. Look for load_all.sql inside the enhanced directory or a subfolder named sql. Copy that file to a convenient location and execute it against your database using your preferred client. For Oracle, you might use:
sqlplus user/password@db @load_all.sql
After the database is ready, adjust the jdo.properties file to point to the correct JDBC URL, driver, user, and password. The DBHelper class will read these values at runtime to create a connection pool.
Finally, run the application. Compile again if necessary to copy the enhanced classes into the runtime classpath:
javac -classpath ".;lib/;enhanced" -d bin src/.java
Then start the program:
java -classpath "bin;lib/*;enhanced" PersonApp
Replace PersonApp with the name of the class containing your main method. When the program runs, it will create three Person records, print them, change one name, delete a record, and print the final list. Each step is wrapped in a transaction, so the database remains consistent even if an error occurs.
To experiment further, try adding a new field to the Person class, recompile, re-run the enhancer, reapply the database script, and modify the PersonPersist code to use the new field. You’ll see that JDO handles the new property automatically, as long as the descriptor is updated accordingly. This demonstrates how the JDO workflow remains consistent even as your domain model evolves.
For those who prefer a build tool, you can translate the steps above into Ant, Maven, or Gradle scripts. The key is to keep the Enhancer invocation after the compilation step and before the application runs. Most JDO projects use a separate build target for enhancement because it must run on bytecode that matches the descriptor.
Download the complete source, including the descriptor, the Person and PersonPersist classes, and the build scripts, from https://www.murdok.org/javapronews/JDO.zip. Unpack the archive, edit the jdo.properties file with your database credentials, and run the commands as described. You’ll see JDO in action without ever writing a line of SQL, and you’ll have a reusable template for your own persistence projects.





No comments yet. Be the first to comment!