Teresa Lau

Subscribe to Teresa Lau: eMailAlertsEmail Alerts
Get Teresa Lau: homepageHomepage mobileMobile rssRSS facebookFacebook twitterTwitter linkedinLinkedIn


Related Topics: Java Developer Magazine

Java Developer : Article

Java Data Object

Java Data Object

Java Data Object (JDO) is a standard API generically used to store, retrieve, and query user-written object classes to and from a data store.

What makes JDO stand out among other persistence options is that it's easy to use and flexible.

JDO provides transparent persistence so it's easy for developers to persist objects without doing any extra work. The tedious and routine persistence work is offloaded to the JDO vendor, leaving developers with time to concentrate on the business logic. In addition, JDO is flexible because it can work on any data store. While JDBC provides persistence only for relational databases, JDO is more generic, providing persistence for any data store, for example, relational databases, files, XML and object databases, etc., making applications very portable.

Overview
Metadata and Enhancer
In JDO, any object class to be persisted needs to be PersistenceCapable, while any class that references a persistence-capable object needs to be PersistenceAware. The good news is that with JDO transparent persistence, you don't have to program your class to implement PersistenceCapable or PersistenceAware. Just write your object class as usual, and the JDO vendor implementation will provide an Enhancer that will make your object class PersistenceCapable based on the metadata you provide. The only extra work you need to do is to write a metadata file in XML for your object. Listing 1 shows the metadata that I'll be using later in my code example. (Listings 1­7 and the code examples for this article can be downloaded from www.sys-con.com/java/sourcec.cfm.)

The metadata is usually short and not hard to write because by default, JDO already derives a lot of information from the object class. Specify information in the metadata only when:

  • You need to override JDO default behavior, e.g., making a field nonpersistent even though it's not transient.
  • There's information JDO can't derive from the class definition, e.g., which field is the primary key or what kind of object is inside a Collection.

    PersistenceManager
    With object classes enhanced, you can then persist the object by using a PersistenceManager. To get a PersistenceManager, first specify a Property object, which usually contains:

    • Data store connectivity information
    • JDO vendor class name
    • Default settings for the PersistenceManager
    Lines 2­3 in Listing 3 show how you can get a PersistenceManager from a JDOPersistenceManagerFactory by specifying a Property object. Once you have a PersistenceManager, you can use it to add, update, delete, and query objects (which I'll discuss in the next section). When you're done, close the PersistenceManager to free up its resources at the end.

    Listing 3 shows code fragments of how you can persist and query objects using JDO. With a PersistenceManager pm, you can add a new object to a data store by makePersistent (Lines 6­8). An object only needs to be made persistent the first time it's known; once an object is already makePersistent, you can update the object directly by referencing its fields. All changes to that object will be saved to persistence storage when the transaction commits. If you don't like those changes, roll back to undo them (Lines 15­17). Similarly, you can delete an object with deletePersistent (Line 26).

    To access objects in the data store, iterate through an Extent, which is the logical representation of all persistent instances of a given persistence-capable class (Lines 12­15).

    However, if you want to be more selective and get only a subset of instances of a given class, create a query. To do this, pass a Candidate object and a Filter to the method newQuery. The Candidate object is a set of objects to select an instance from; it can be a collection of objects or an extent. The filter is a string written in JDO Query Language (JDOQL). Once you create the query, execute it and get back a collection of instances that match (Lines 22­26). JDOQL is the query language for JDO; it's somewhat similar to SQL but has a Java syntax. The example here is a simple one; with JDOQL, your Filter string can be much more sophisticated. In addition, if you declare parameters to act as placeholders in the filter string, you can write a single query and then execute it multiple times, supplying new values each time. There's a lot more to JDOQL that you can read up on; please refer to the links in the Resources section.

    A unique thing about JDO's query capability is that once you get an object, you can navigate to any other object referenced by that object. Frequently, all you need is to get a starting point object, and you can already get to any object related to it without having to run another query.

    An Object Persistence Example
    To find out if JDO is as good as promised, I'll write some code using JDO and JDBC to persist a Book object that I created (see Listing 4). This Book object contains a name and a Block object. To make things interesting, a Book has a constraint that each book is uniquely defined by its name, meaning that you're not allowed to add two books with the same name.

    A Block is a building block for a Book. It can be of type Document, Chapter, or Section. The root Block is of type Document and can contain any number of Chapter Blocks. Each Chapter Block can also contain any number of Section Blocks, so there's a recursive relationship here for Blocks. Within each Block, there's a HashMap that may store any number of attribute value pairs for that Block.

    Listing 5 shows a test Book I created for my example. This Book contains two chapters: Chapter 1 has one section and Chapter 2, two. In particular, Chapter 2 has an attribute Color=Red.

    Using this Book class, I want to implement some common persistence features as follows:

  • Add: See if I can successfully add two books to a data store, and that when I add a third book with the same name, an exception is thrown because the integrity constraint is violated for the unique book name.
  • Update: See if I can update a Book by adding an attribute "Comment" to its root Block. When I commit, the change should be saved, and if I roll back, the change should be discarded.
  • Delete: See if I can look up a book by a query and delete it from the data store.

    Without JDO, my usual way to implement this would be to design relational database tables to store all the data contained in a Book, then use SQL and JDBC to store/retrieve that data to/from the tables. Due to space constraints, I won't show you my JDBC implementation here, but you can download it from the JDJ Web site if you're interested. Note that to implement the above features in JDBC/SQL, I have to write quite a lot of code (480 lines!). What I'm going to show you now is a much shorter way to address the same problem using JDO.

    Persisting a Book Object Using JDO
    To persist a Book object in JDO, although I'm using exactly the same object class as I would use if I implemented this in JDBC, the ID field in the Block object is now unnecessary. If I implement this in JDBC, the ID field would be needed to internally reference a different Block from the database table. However, using JDO, I don't have to worry about populating this field since JDO will handle that internally.

    To persist my Book object, I create metadata for both a Book and a Block (see Listing 1). In the metadata for Block, I specify that the object in the Collection children is of type Block (Lines 10­11), while the key and value for HashMap attributes are of type String (Lines 12­14). Also, since the ID field is not really needed for the Block object, I specify in the metadata that there's no need to persist it (Line 8). In the metadata for Book, I specify that nm is the primary key of Book (Line 5), and that the Book object should use my user-defined Application Identity BookKey as the object identity class (Line 4). The code for the class BookKey can be found in Listing 6.

    In this example, the JDO Vendor implementation I use is Kodo JDO (which uses a relational database). There are many JDO implementations in the market; you can choose to use any of them, and your application code doesn't need to be changed. For the data store, I use Enhydra InstantDB (a relational database included with the Kodo distribution). The essence of JDO is that the developer does not need to know how the vendors persist data to a database, so I don't need to design any table here even though we are using a relational database behind the scenes. The vendor Kodo provides a tool called schematool to help me create those tables based on my metadata. All I need to do now is run the following to prepare the database for my object.

    schematool ­action refresh Book
    schematool ­action refresh Block

    Next I compile my object classes as usual, then using Kodo's enhancer tool jdoc, I enhance my class file by running:

    jdoc.Book
    jdoc.Block
    jdoc.BookPersistJDO

    Here, as long as I put the unenhanced class files (Book.class, BlockPersist.class, BookPersistJDO.class) and the metadata file in a location where jdoc can find it, jdoc will then modify the bytecode of these classes to add methods necessary to make them PersistenceCapable or PersistenceAware. Classes with metadata will be enhanced to PersistenceCapable, while classes without metadata will be enhanced to PersistenceAware. In this example, Book.class and Block.class are enhanced to be PersistenceCapable while BookPersistJDO.class is enhanced to be PersistenceAware.

    Once I've enhanced my code, any persistence work for my object can be done via the PersistenceManager. Using the code I showed you previously, I can easily get a PersistenceManager pm and then add, delete, or update a book using it. Listing 7 shows fragments of my code BookPersistJDO.java. The method addBook (Line 3) shows how I add a Book in JDO, and the method deleteBook (Line 13) shows how I delete a book.

    To get a Book with a given name, I create a query using a Filter written with JDOQL. Executing the query, I get back a Collection of Book object that matches this query (Lines 25­29). Once I get a Book object, I directly update its field and then commit the changes.

    Figure 3 shows the results of running my test on this implementation. The results are as I expected. First, I successfully added books to the data store, and when I add a book with the same name, it gives me a JDOUserException, showing that the unique book name integrity is violated. Next, I'm able to update a book, then keep the changes by committing, or discard the changes by rolling back. Last, I'm able to query the data store to get a book by its name, and then delete the book from the data store.

    Comparison of Implementations
    By using JDO and JDBC to tackle the same problem of persisting a Book object, I observed the following:
    1.   Using JDO, I'm able to achieve the same things I can achieve with JDBC. I can query objects using JDOQL; maintain data integrity by specifying the nm of a Book as a unique primary key; and add, delete, and update my object.
    2.   JDO makes my transaction handling easier. In my JDBC implementation, because one Book object actually translates to many rows in four different relational tables, I have to ensure that all inserts or deletes to tables are done in a transaction. On the contrary, JDO saves or deletes the whole object to the database in one operation, and I don't need to use transactions to maintain the atomicity of the action.
    3.   The fact that BookPersistJDO.java (140 lines) is so much shorter than BookPersistJDBC.java (480 lines) shows that JDO simplifies my code a lot. This is especially so given the recursive nature of my object and how complicated it is to represent it in relational tables. In my JDBC implementation, I have to put a lot of thought into the design of my tables so I can store and retrieve the recursive data. I have to generate an ID for each Block to use as a link for the child/parent relationship. In the JDO implementation, I don't have to think about any of that, and everything is already persisted correctly.
    4.   The effort I put into maintaining a cache for performance in my JDBC implementation is not needed in JDO, because the JDO vendors are the ones who would implement data caching for performance improvement. That saves me a lot of work because I don't have to worry about keeping my cache in sync with the database all the time.

    Behind the scenes, the JDO vendor's implementation and my JDBC implementation are probably not much different. For example, this JDO vendor, using a relational database, may also have implemented it with a similar table design and ID generation scheme, and used JDBC to persist the data. However, the important point is I don't have to know about these implementation details: they're all offloaded to the JDO vendor who has expertise in that area, and who would likely implement it better. In addition, the vendor is also free to implement it with any other kind of data store like object databases and files, giving us more flexibility and choices on where to store the data.

    Conclusion
    JDO offers a lot of advantages for developers:

  • It has all the basic functionalities needed for data persistence: add, delete, update, transaction, data integrity, and data caching.
  • It takes over a lot of tedious work from the developer, making code easy and maintainable.
  • It's vendor independent, preventing vendor lock in.
  • Although my example doesn't show it, it can work on any data store, making development flexible and portable.

    JDO is a technology that's worth exploring. This article is a starting point for you; for more information see the resources section.

    Resources

  • The One-Stop Site for JDO: www.jdocentral.com
  • The Specification: http://access1.sun.com/jdo/
  • Roos, R. (2002). Java Data Objects. Addison Wesley: www.OgilviePartners.com
  • The JDO Vendor I used in my example: SolarMetric: www.solarmetric.com/Software/Kodo_JDO

    Properties for JDO
    Listing 2 provides the Property file that I'll use later. I'm using a relational data store and Lines 1­4 contain JDBC connectivity information. Line 6 contains the name of the JDO vendor class I'm using. Lines 8­10 are the default settings I want for the PersistenceManager. It specifies that I'm using optimistic transaction, and that values in the cache should be restored on a transaction rollback, and should not be retained on a commit. There are many more details on how you can use different options for transactions; read the JDO API to find out what they all mean and when to use them.

    JDBC Implementation
    Here I'll briefly describe how I implement the persistence of Book using JDBC. By knowing how much work is involved in this, you can better appreciate how much JDO does for you.

    To persist Book to a relational database, I created four tables (see Figure 1). Based on data integrity, the nm field in the Book table is defined as a unique key. To trace the relation between each Block to its children and attributes, I assign a blockId to each Block. This blockId is generated (by incrementing 1 to the largest blockId in the Block table) every time a new Block is added.

    Adding a Book object involves denormalizing the information within a Book object into four tables: Book, Block, BlockRelation, and BlockAttr. In particular, a blockId has to be generated for each Block. Furthermore, the tedious part is that Blocks are recursive, and I have to write code that recursively inserts the Block data.

    For the example Book created in Listing 5, the tables will be populated as shown in Figure 2. As you can see , one Book is translated into many rows in four tables, so all inserts have to be wrapped in a transaction so that a Book is added all or none, never partially. Similarly, deleting a Book involves finding the appropriate rows to delete in each of the four tables, while wrapping all deletes in a transaction so that a Book is deleted all or none.

    For better performance, I don't want to query four tables to recursively generate the Book object every time someone asks for a Book. Instead I created a BookCache that I populated at the beginning by generating all Book and Block objects from the four tables. As I add, delete, and update Books in the database, my code ensures that the BookCache is kept in sync. As a result of all this work done in maintaining a cache, to get a Book is as easy as looking up the cache by its name.

    Not Yet Perfect
    Despite all its advantages, there are still areas where JDO may not be perfect yet:

  • It's good for new development, but to convert existing schema in a relational database to use JDO requires a bit of mapping work.
  • As developers, we no longer deal with lower-level database access when using JDO, so it may be hard for us to tune performance. Since JDO implementation has to do a lot of extra work tracking the fields that are changed or synchronizing cache internally, etc., how well the JDO vendor implements it will be critical to performance.
  • JDOQL does not have an aggregate function such as max, min, and sum like SQL does.
  • It would be nice if there were better checking to catch bad JDOQL at compile time. For example, in a Filter, when you specify the field name in the class via a string, you can easily have specified a bad field name resulting in a JDOQL that will compile, but fail at execute time.
  • Some people thought that JDO's advantage is that you don't have to write SQL anymore; the truth is now you have to learn to write JDOQL instead!
  • More Stories By Teresa Lau

    Teresa Lau has been an independent Java consultant for over four years, with
    an emphasis on financial applications. She received her MS in computer
    science from the University of Waterloo, and her BS in engineering from the
    University of California, Berkeley.

    Comments (17) View Comments

    Share your thoughts on this story.

    Add your comment
    You must be signed in to add a comment. Sign-in | Register

    In accordance with our Comment Policy, we encourage comments that are on topic, relevant and to-the-point. We will remove comments that include profanity, personal attacks, racial slurs, threats of violence, or other inappropriate material that violates our Terms and Conditions, and will block users who make repeated violations. We ask all readers to expect diversity of opinion and to treat one another with dignity and respect.


    Most Recent Comments
    Serguei Bakhteiarov 07/02/03 04:05:00 PM EDT

    I did work with TopLink three years ago and by then it was the best tool I had ever used for O/R after EOF from NeXTSTEP.

    Now, regarding locking to Oracle as a vendor. TopLink supports whole bunch of other DB engines, for all Oracle's competitors. I do not remember exactly, but if you have JDBC driver, -you can use TopLink (I am not sure about ODBC-JDBC bridge).

    Now, about TopLink does not fully implement JDO. Well, TopLink engine is much MORE superior then the proposed JDO spec.... It actually leads the JDO spec forward.

    I am also torn as I write which way to go and I guess again the major decision maker is going to be.... the price.

    serge

    disclaimer:
    I do/did not work for ObjectPeople/Oracle and such ever.

    wafd 05/20/03 12:15:00 AM EDT

    hello all

    Roger 05/06/03 03:43:00 PM EDT

    HashMap

    Roger 05/06/03 03:43:00 PM EDT

    HashMap

    Jack Bonda 04/21/03 05:31:00 PM EDT

    JDO sounds really promising, except it might go the same wasy as the ODMG initiatives. There does need to be buy ini from the pig players such as BEA,IBM, Oracle and Sun. But they may have a problem doing that, as supporting this spec will be viewed as backtracking on the EJB spec.

    David Jordan 04/14/03 09:02:00 PM EDT

    Oracle is trying to sell the current TopLink product, which does not support JDO fully. They will not tell people to use JDO until they have an implementation they can sell you. This is standard business practice. But TopLink was on the path to supporting JDO. Oracle is also a member of the JCP executive council and they voted in favor of JDO. But face it, with JDO, you become independent of the database vendor. Do you expect Oracle to like this? No. But they will have to support it once enough people demand it. All the non-JDO OR mapping vendors are criticizing JDO. Why? Because it competes with their existing product base. It all comes down to money, even if they try to throw out technical reasons. I have never used TopLink. But people who have used it say that JDO is much easier to work with.

    David Jordan 04/14/03 08:58:00 PM EDT
    Siva 03/27/03 01:25:00 AM EST

    JDO API provides more easy implementation with fast access. Most of the companies are switching over to Open Source database(s) and Platform(s) for their development. I can know very well, companies shifted their database from oracle to mysql, b'caz of Oracle's high price in multiuser licence. Now JDO provides more advantage for this type of shifting.

    You can find a list of JDO implementations at www.jdocentral.com.

    And companies started releasing tool(s) for JDO.

    abc 03/27/03 01:13:00 AM EST

    abc

    Alf 03/25/03 09:57:00 AM EST

    It is a matter of time. You will see that programmers prefer JDO , and ORACLE will have to offer their own implementation of the JDO Specification .
    You CAN

    Chester Arnold 03/24/03 07:49:00 AM EST

    I agree with David Tinker. You need to learn to try things out and not always believe vendors who are trying to salvage tanking stock prices. I've used a number of JDO implementations including the one the article used (Kodo JDO). The quality of JDO implementations vary widely so trying them out is probably the best thing you can do.

    You can find a list of JDO implementations at www.jdocentral.com.

    David Tinker 03/14/03 08:26:00 AM EST

    Treat anything that anyone associated with Oracle has to say about JDO with caution. Oracle own Toplink, a $10K/CPU O/R mapping tool that competes directly with JDO. If JDO is sucessful who will pay that kind of money for vendor-lock-in O/R mapping? The most expensive JDO implementations are approx $3K with no runtime costs.

    I won't say anything about JDO as I work on JDO Genie from Hemisphere Technologies :) Try it for yourself!

    bret 03/09/03 11:22:00 PM EST

    http://www.sys-con.com/java/article.cfm?id=1899

    Scott P. Smith 03/05/03 10:12:00 AM EST

    Another thing that Donald Smith (no relation) from Oracle said was that the only people making positive comments about JDO are JDO vendors. I see "I Davie" is with Versant, which is soon to be a JDO vendor according to their web site.

    I am very neutral with regard to JDO. I have never used it. Can we hear comments from someone (who doesn

    I Davie 03/05/03 04:51:00 AM EST

    JDO is meant to be a Java standard not a database standard. Oracle are not interested in JDO because it gives developers choice and Oracle don't like that! If they're forced to move that way they will but not by choice :-)

    Donald Bales 03/04/03 07:39:00 PM EST

    The whole article became tainted when the author stated: "...JDBC provides persistence only for relational databases..." Not true. JDBC can handle most of the data sources listed. In fact, many JDO implementations utilize JDBC "under the covers". Second, JDO claims to be able to support these other data sources, but I don't see many implementations. JDO provides a slick abstration for relational databases, but it's not the silver bullet that it's hyped to be.

    Scott P. Smith 03/04/03 01:27:00 PM EST

    A few weeks ago, I attended a one hour talk by Oracle's Director of Technology, Donald Smith covering persistence archetectures. In it he strongly stated that JDO is not completely vender independent. I don't know that myself. I'm just repeating what he said, which conflicts with claims made in this article.