In a past article on XML Databinding, I showed
how you could work with Java objects and have them persist as XML files.
In this article, I’ll discuss Sun’s Java Data Objects (JDO) standard. JDO allows you to persist Java objects, supporting transactions and multiple users. It differs from JDBC in that you don’t have to think about SQL and “all that database stuff.” It differs from serialization as it allows multiple users and transactions. This standard allows Java developers to use their object model as a data model, too. There is no need to spend time going between the “data” side and the “object” side.
Various products — including CocoBase, WebGain TOPLink, and Castor JDO — try to do this for you. Now that there is a standard way to do this, we get the benefit of only having to learn one way to do it. This is just like when JDBC came along and allowed us to work with any database that provided a driver. Great stuff!
We will take the Address Book example from my XML data binding article and “port” it over the JDO world. The JDO implementation that I will use is OpenFusion JDO from Prism Technologies. As you’ll see, there is only one part of the code that uses a PrismTech API; everything else uses the standard JDO API (with the implementation hidden behind the scenes).
We will walk through the following tasks:
- Data Object: Create the
Person
object that we wish to persist in the database. - Persist: Create a
PersonPersist
object that will take care of persisting, reading,
and updating thePerson
s in the datastore. - JDOEnhancer: Create a JDO descriptor to tell the JDOEnhancer what we are doing.
- Build: Go through the steps to build and run the system.
Data Object: Create the Person Object
We’ll start with the same Person
object we defined in the XML article. This object follows the standard JavaBean conventions of get
and set
on the attributes. Notice that although we are persisting this class, there is nothing special about it. It doesn’t have to inherit or implement any persistent interface/base class. The requirements for a class that can be persisted are:
- Fields must be accessible to the JDO classes (
public
, orset*
methods). - The field’s data type must be permitted in accordance with the JDO spec (see section 6.4 for detailed information).
- Certain fields can not be supported (unserializable ones like
Thread
,File
,Socket
).
With these requirements in mind, let’s look at Person.java
:
-==-
Persist: Create a PersonPersist Object to Manage Persistence
So we have a Person
object that we want to work with. Now we need to create something that will manage the persistence. Let’s walk through the code, and see how we:
- Initialize the JDO Persistence Manager.
- Persist three “people” to the database.
- Display the people from the database.
- Change the name of one of the people.
- Delete a person.
- Run through these things in the
main()
method.
Step One: Initialize the JDO Persistence Manager
Here we have the beginning of the PersonPersist
object. We import the standard JDO classes and the ManagedConnectionFactory
from the OpenFusion implementation. We could have abstracted that out to a separate class, of course. The constructor sets the connection factory, using the javax.jdo.PersistenceManagerFactoryClass
property. This is like setting the database driver property in the JDBC world.
-==-
The connection factory is created in a static method named createConnectionFactory()
. This factory needs the JDBC URL, the JDBC driver, a username, and a password. OpenFusion JDO also comes with a DBHelper
that looks for a properties file containing the database settings to use.
-==-
Step Two: Persist Three “People” to the Database
Here we have some real meat. The persistPeople()
method creates three people, using the constructor that we saw in the Person.java
file. Then we see JDO at work. The first thing we do is get a persistence manager via getPersistenceManager()
. Then we create a transaction where we will do our work (and we commit that work after we do it). To persist this object graph we simply call the makePersistentAll( Object[] )
method. The for()
loop at the bottom of the code gets the unique ID for the persistent objects, and saves them away for later use.
-==-
Here are some of the other methods that you can call on the persistence manager. The three categories are:
- Make instances persistent. Take a transient object and persist it.
- Delete persistent instances. Delete the information from the datastore.
- Make instances transient. Disassociate the instances from the persistence manager. The datastore doesn’t have its information deleted.
Make instances persistent Delete persistent instances Make instances transient makePersistent(Object o)
deletePersistent(Object o)
makeTransient(Object o)
makePersistentAll(Object[] os)
deletePersistentAll(Object[] os)
makeTransientAll(Object[] os)
makePersistentAll(Collection os)
deletePersistentAll(Collection os)
makeTransientAll(Collection os)
Step Three: Display the “People” From the Database
Our display code starts by getting the persistence manager (as all the code will do). We use the object IDs that we saved in the persistPeople()
method above to give us our object back. Once we have our object, we can call the methods that the object implements — in this case get
s to give us our data back. At this point you are probably seeing there isn’t a lot of code needed to persist your objects.
-==-
Step Four: Change the Name of One of the People
The code to change a Person
that exists in the datastore is simple, too. It should look very similar to the code to display the “people.” Here we are creating a transaction (since we are modifying the row), changing the name using the setName()
method that we defined, and finally, committing the transaction to save the changes back. The only real difference between this operation and working with transient objects is that we are thinking about transactions.
-==-
Step Five: Delete a Person
Could you have guessed the code needed to delete the second person from the datastore? You know all of the information. Looking at the code below you will see that we are using the deletePersistent()
method mentioned in the persistence manager methods in Step Two.
-==-
Step Five: Run Through These Things in the main() method
Finally, this program has a main()
to run through, persist the people, change one, and then delete it. If you run this, you will see the address book displayed at each point.
-==-
JDOEnhancer: Create a JDO Descriptor for the JDOEnhancer
We now have all the code for our application. The next step that we need is to create a JDO descriptor that the JDOEnhancer will use. “What is the JDOEnhancer?”, I hear you scream. The JDO architecture is built with the idea that a JDO implementation can take the bytecode for your classes and manipulate them to add needed functionality. For example, the JDOEnhancer will make the class implement the PersistanceCapable
interface (so you don’t have to), and may implement some of the methods in that interface. So we will see that after we compile our code, we will have to run the JDOEnhancer to do the bytecode manipulation (this is something that Thought Inc. doesn’t like about JDO). We need to create a descriptor file that gives information about the classes that we wish to persist. The file looks like this:
-==-
This is a basic file, but works for our needs. There is more complicated mapping available, and you can check the spec at Section 18: XML Metadata. Here is a slightly more complex mapping from the OpenFusion examples:
-==-
Now that we have the code and the JDO descriptor, let’s put it all together and see how to build the system.
Build: Go Through the Steps to Build and Run the System
There are only a couple of steps to build the system that we have created:
- Compile the code.
- Run the JDOEnhancer.
- Set up the database (using output from the JDOEnhancer).
- Run the application.
Step One: Compile the Code
We all know how to run javac
right? We just have to make sure that we set up the CLASSPATH
correctly before we run it. Here is an example running Windows:
-==-
Step Two: Run the JDOEnhancer
The JDOEnhancer takes the compiled code that we just created and the JDO descriptor file that we built previously. Here is the full syntax of the OpenFusion JDOEnhancer:
-==-
Here is an example of running the JDOEnhancer for our application:
-==-
Step Three: Set Up the Database (Using Output From the JDOEnhancer)
The JDOEnhancer can create database scripts to set up the database for us, as long as we give it the -db
and -od
switches. It will create lots of separate scripts, but one will be called load_all.sql
. Open that file and load it into your favorite SQL prompt (e.g., sqlplus
for Oracle).
Take a peek at the Oracle version that is created for our application:
-==-
Step Four: Run the Application
Now the database is set up; we can run the application and see it roll!
-==-
Grab the code and try it out!
Conclusion
We have shown how to work with the new JDO standard, using the OpenFusion JDO implementation. This is a new world, where developers can focus on their business needs and work with objects without having to be SQL gurus. I hope JDO takes off, even though some people in the industry think that the spec isn’t quite there yet.
First appeared at O’Reilly’s OnJava.com
Dion Almaer is Principal Technologist with The Middleware Company, and
Chief Architect of TheServerSide.com: Your J2EE
Community. Dion frequently writes and speaks on his experiences, in
various print media, and at ONJava.com. He also blogs about life, the universe,
and everything tech.