ONJava.com    
 Published on ONJava.com (http://www.onjava.com/)
 See this if you're having trouble printing code examples


Flawed Understanding of JDO Leads to FUD

by David Jordan, coauthor of Java Data Objects
05/29/2002

When a new technology comes along that introduces a paradigm shift, it often challenges the existing, established technology in use. It is a natural tendency for those with a vested interest in maintaining the status quo to spread fear, uncertainty, and doubt (FUD) about the new technology. Java Data Objects (JDO) is a new technology released from the Java Community Process that allows developers to directly persist their object models in transactional datastores. It supports the development of applications where the Java object model serves as the sole data model used in the application. It provides a query facility that allows applications to filter the objects in their object model using query operators and syntax taken directly from Java. JDBC and SQL are noticeably absent from JDO's interface, yet it has been designed so that it can be layered on top of these relational database technologies.

Donald Bales recently wrote an article for OnJava.com titled "Flawed JDO Points the Way to the Objectbase." In this article, he brings up issues he has with JDO and then presents an application program that is supposed to provide an "apples-to-apples" comparison between JDBC and JDO. He provides an Oracle-specific JDBC program that is similar, yet different, from an article by Dion Almaer titled "Using Java Data Objects."

Bales' program does not provide the same functionality as that found in Almaer's article, so it is not a true apples-to-apples comparison. In the interest of brevity, I will not enumerate all of the differences among the programs in these two prior articles; however, I will provide a complete, compiled, and working JDO program that does correspond to the program provided in Bales' article.

Before presenting the application, I'd like to address some statements Bales made in his article. He claims:

JDO threatens the livelihood of products such as object/relational mapping utilities that map Java objects to relational data. Because of this, and for other reasons, JDO has received more than its fair share of bad press.

Unfortunately, no references are provided to support his claim that JDO has received a lot of bad press. I am only aware of one such negative commentary in the press. It comes directly from one of the object/relational mapping vendors of a product that maps Java objects to relational data. This vendor has their own product with a proprietary API that stands to lose significantly if JDO succeeds. To those familiar with the situation, it is well understood that the attacks are motivated by the company's financial concerns. JDO provides a standard Java interface to support object/relational (OR) mapping. It only threatens existing OR products based on proprietary interfaces that will lose favor as JDO adoption accelerates. It is very common for companies with proprietary solutions to raise criticisms of a new standard that makes their current solution obsolete.

Bales states he has two issues with JDO. First, he claims that "somehow SQL has become a bad thing." I am not sure how this suggests a flaw in JDO. JDO proponents are not stating SQL is bad. JDO merely provides an alternative, Java-centric means of persisting information in a database and does not use SQL in its interface. Many of the JDO implementations available in the market are implemented on top of JDBC and SQL. SQL provides capabilities that JDO does not, and probably never will, provide. SQL provides support for the relational data model, allowing it to dynamically associate "formerly unrelated populations of information," as Bales claims. For many applications, the facilities of SQL are essential. But many Java developers want to have a Java object model view of their information. They have struggled due to a lack of a standard for persisting their Java object models in a database. JDO provides an alternative to JDBC/SQL more suited for these applications.

Bales' second issue with JDO is his belief that JDO treats a database management system (DBMS) like a file. He claims that with JDO you open a file, read it all into your application, write changes back to disk, and close the file. He states that this "does not properly facilitate multi-user access." It is not clear where he got this misunderstanding of JDO. The "Using JDO" article that Bales references used Oracle, the same database Bales uses in his example. While JDO implementations exist that are built directly on top of the file system, implementations are available for object databases and most relational databases. JDO has been architected to allow updates to be made at the granularity of a single field of a single instance in the database. So there is nothing inherent in JDO that precludes multi-user access.

Bales states that JDO is a "workaround" and that:

... [O]bject-oriented programmers ... have not applied it to solving the business problems. ... (They are) stuck in the world of functional decomposition ... abstracting things in the real world by categorizing information about them, but ignore their behaviors.

First of all, I whole-heartedly disagree with his characterization of object-oriented programmers. And it is very unclear how this statement is related to JDO being a workaround. JDO allows one to build object models and software that are completely orthogonal to the ways in which they are composed and used in applications, with many different decompositions of the application's architecture, functional or otherwise.

Java Data Objects

Related Reading

Java Data Objects
By David Jordan, Craig Russell

Bales then proposes that what we need is an "objectbase" instead of a database, where binary executables can be stored and retrieved. He then suggests that the way to do this is to use JDBC or SQLJ. In particular, he advocates using Oracle's specific implementation of OR mapping. One of the goals of JDO is binary portability of applications across all underlying datastores. A JDO implementation is free to optimize its mapping of Java models onto the underlying Oracle OR feature set. With JDO this mapping is hidden from the application, which allows it to be portable across a wide variety of databases.

Example Application

I will now discuss my experiences of reviewing Bales' program and my development of an equivalent program with JDO. Bales' first step is to create a user-defined type (UDT) using a SQL CREATE TYPE command and then create an associated object table. It is not necessary to provide these underlying datastore data definitions using most JDO implementations. Many of the JDO implementations automatically generate these data definitions for the Java application. If there is an existing set of tables, implementations provide a means to define the mapping between your Java object model and the underlying database.

Next Bales introduces a Person class. It is necessary for his class to import other classes from java.io and java.sql. His class needs to implement the SQLData interface, using SQLInput and SQLOutput streams. In JDO, it is not necessary to import such types or implement the methods that marshal and demarshal the object. I used his program as the starting point for my implementation of the program in JDO. The definition of his data members included the following:

private String = name;
private String = address;
private String = ssn;
// etc.

Another problem I discovered was the following method definition:

public void setEmail(String Email) {
  this.email = email;
}

I discovered this when my email address seemed to always be set to null. The reason for this is that the name of the parameter (Email) is not being assigned to the field itself. These two pieces of code prevent his program from being compiled and tested successfully. The equivalent JDO application that I provide here has been compiled and tested.

The Persistent Class Person

The following class, Person, is the class to be stored in the database. This is the same class defined in Bales' article, with the above corrections made and also removing code not necessary when using JDO.

package onjava;
public class Person {
  private String name;
  private String address;
  private String ssn;
  private String email;
  private String home_phone;
  private String work_phone;
 
  public Person() {
  }
  // Accessors
  public String getName() {
    return name;
  }
  public String getAddress() {
    return address;
  }
  public String getSsn() {
    return ssn;
  }
  public String getEmail() {
    return email;
  }
  public String getHomePhone() {
    return home_phone;
  }
  public String getWorkPhone() {
    return work_phone;
  }
  // Mutators
  public void setName(String name) {
    this.name  = name;
  }
  public void setAddress(String address) {
    this.address  = address;
  }
  public void setSsn(String ssn) {
    this.ssn  = ssn;
  }
  public void setEmail(String email) {
    this.email  = email;
  }
  public void setHomePhone(String homePhone) {
    home_phone  = homePhone;
  }
  public void setWorkPhone(String workPhone) {
    work_phone  = workPhone;
  }
}

Bales' Person class was 78 lines long. The class defined above has the same functionality in only 50 lines. The savings comes from the fact that with JDO I do not need to provide the methods readSQL, writeSQL, and getSQLTypeName. As Bales notes himself, this code is tedious. It could also be error-prone. With a JDO implementation, this same functionality is automatically generated for my class. So the same functionality is necessary, but with JDO this code is generated for the application, reduces the developer's workload, and avoids error-prone code.

The Enhancer and Metadata

Specifically, the code is added by an enhancer directly to my .class file. The enhancer is given the file Person.class and a JDO metadata file that lists the persistent classes and any other persistence-related information not expressible in Java. For this very simple schema, the metadata is trivial:

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE jdo SYSTEM "jdo.dtd">
<jdo>
  <package name="onjava" >
    <class name="Person" />
  </package>
</jdo>

The enhancer adds the code necessary to provide transparent persistence of the class, including methods needed to migrate data between the object and the database. The JDO enhancement contract for performing this is designed in such a manner that the code placed in my class is portable and will work with any JDO implementation. The resulting .class file is vendor-neutral.

The Application

Below I provide the application program that performs the same operations as provided in the DemonstrateOR program written by Bales.

package onjava;
import javax.jdo.*;
import java.util.*;
import java.io.*;
/**
A program to demonstrate JDO
*/
public class DemonstrateJDO {
    PersistenceManager pm;
    Transaction tx;
 
public DemonstrateJDO() {
    PersistenceManagerFactory pmf = null;
    try {
        InputStream propStream =
                      new FileInputStream("jdo.properties");
        Properties props = new Properties();
        props.load(propStream);
        pmf = JDOHelper.getPersistenceManagerFactory(props);
    } catch(Exception ex){
        System.out.print("Error creating PMF");
        System.out.println(ex);
        System.exit(1);
    }
    pm = pmf.getPersistenceManager();
    tx = pm.currentTransaction();
}
 
public static void main(String[] args)
               throws Exception {
    new DemonstrateJDO().process();
}
 
public void process(){
    Person person = null;
    // Insert a person object
    try {
      // create a new instance
        person  = new Person();
        person.setName("Bales, Donald J.");
        person.setAddress("Downers Grove, IL 60516");
        person.setSsn("999-99-9999");
        person.setEmail("balesd@compuserve.com");
        person.setHomePhone("(999) 999-9999");
        person.setWorkPhone("(999) 999-9999");
        tx.begin();
        pm.makePersistent(person);
        tx.commit();
    }
    catch (JDOException e) {
      System.err.println("JDO Error: "+e.getMessage());
      System.exit(1);
    }
 
    // Update the object using standard JDO
    try {
        // Let's change my email address
        tx.begin();
        person.setEmail("don@donaldbales.com");
        tx.commit();
    }
    catch (JDOException e) {
        System.err.println("JDO Error: "+e.getMessage());
        System.exit(1);
    }
 
    try {
        // Print the Persons in the database
        tx.begin();
        Extent persons = pm.getExtent(Person.class,true);
        Iterator personIter = persons.iterator();
        while( personIter.hasNext() ){
            person = (Person) personIter.next();
            System.out.println("name:       " +
                    person.getName());
            System.out.println("address:    " +
                    person.getAddress());
            System.out.println("ssn:        " +
                    person.getSsn());
            System.out.println("email:      " +
                    person.getEmail());
            System.out.println("home phone: " +
                    person.getHomePhone());
            System.out.println("work_phone: " +
                    person.getWorkPhone());
        }
        persons.close(personIter);
        tx.commit();
    }
    catch (JDOException e) {
      System.err.println("JDO Error: "+e.getMessage());
      System.exit(1);
    }
 
    // Delete the person
    try {
        tx.begin();
        Extent persons = pm.getExtent(Person.class,true);
        Query query = pm.newQuery(persons,
                               "ssn == \"999-99-9999\"");
        Collection rslt = (Collection) query.execute();
        Iterator iter = rslt.iterator();
        person = (Person) iter.next();
        query.close(rslt);
        pm.deletePersistent(person);
        tx.commit();
    }
    catch (JDOException e) {
      System.err.println("JDO Error: "+e.getMessage());
      System.exit(1);
    }
  }
 
  protected void finalize()
   throws Throwable {
    if (pm != null)
      try { pm.close(); }catch (JDOException ignore){ }
    super.finalize();
  }
}

Once again, the JDO program is smaller. DemonstrateOR is 197 lines of code, my DemonstrateJDO program is 119 lines. Not only is the JDO program smaller, it also seems simpler to me. But deciding which program is simpler is likely a subjective opinion. You can decide for yourself which program is easier to understand.

Establishing a Database Connection

In Bales' program, the constructor registered a driver and established a connection with the database. I have provided similar code, except with JDO it is necessary to construct instances of PersistenceManager and Transaction for interacting with the database.

The PersistenceManager is created and configured with a PersistenceManagerFactory. One can place the properties needed to initialize the PersistenceManager in a property file, which I have called jdo.properties. The following lines can be placed in this property file:

javax.jdo.PersistenceManagerFactoryClass=vendors_pmf_class_name
javax.jdo.option.ConnectionURL=jdbc:oracle:thin:@DAVESLAPTOP:1521:dave
javax.jdo.option.ConnectionDriverName=oracle.jdbc.driver.OracleDriver
javax.jdo.option.ConnectionUserName=scott
javax.jdo.option.ConnectionPassword=tiger

The first property identifies a vendor-specific name of a PersistenceManagerFactory class. Only this one property value must be changed to switch from one vendor implementation to another. The other properties are virtually identical to the values needed in Bales' program to initialize his JDBC driver and connection.

In the main method of Bales' program, he starts by updating the connection's type map, associating the Java class with its corresponding SQL user-defined type. This is not necessary with JDO. This registration code is automatically added to the Person class by the enhancer and is executed when the class is first loaded into the JVM.

Making an Instance Persistent

The next step in the application is to create an instance of Person. The code to initialize the Person instance with data is identical in the two programs. To persist the Person instance in the JDO application only requires a single line of code:

pm.makePersistent(person);

In Bales' program, it is necessary to reference the underlying table in the relational database. The developer is forced to deal with both the Java data model and the relational data model. There is an opportunity for trouble here if the wrong table is used. With JDO this is not necessary; the developer only needs to use the Java data model. This also makes the JDO application more portable, as it can work across many different database architectures.

Updating an Instance

The next step is to update the Person instance. In JDO, if we have a reference to the Person instance after committing a transaction, the identity of the referenced instance is maintained. If the reference is used in a new transaction, the identity is used to access the instance in the database. My code demonstrates this capability. To update the email address, we simply invoke the setEmail method.

person.setEmail("don@donaldbales.com");

When the transaction commits, any updates to objects get automatically propagated to the database without any coding by the developer necessary. Once again, the JDO code to perform the operation is a single line of code. Can it get any simpler than that? This one line of JDO code corresponds to 25 lines in Bales' program. Half of Bales' code here deals with committing the update to the database. The other half deals with issuing a query to access the instance. I could have performed a query in JDO; I demonstrate this later when we delete the instance. Many lines of code are necessary in Bales' program to set up SQL statements to be processed. None of these lines of code need to be written in a JDO application program. The same functionality is provided by the underlying JDO implementation if it is providing access to an SQL database. But by using the JDO API there is less work necessary for the application and no dependencies on the specific underlying database being used.

Printing Information About All Instances

The next step performed in the two applications is the printing of information about all of the Person instances in the database. The two programs are fairly similar with this step. In JDO, you access an Extent, which provides access to all of the instances of a class. You then use a standard java.util.Iterator to iterate through each of the instances. With JDBC, you create a statement to perform a select on a table and you execute the query. It returns a ResultSet, which has its own built-in methods to iterate through each instance.

Perhaps now would be a good time to raise a question concerning Bales' proposed solution. In JDO, within the context of a transaction, the PersistenceManager guarantees that each unique instance in the database is represented just once in the application's memory. If the application performed several different queries or navigated from several different places in the data model and referenced the same instance in the database, the application would always get a reference to a single instance in memory.

It is extremely important for the application to have only a single copy of each instance from the database in a transaction. Otherwise, if distinct updates were made to the different copies of the same persistent instance, there is an issue of migrating these updates to the database. If each separate instance is written to the database, the same object in the database would get updated multiple times, and the last update made would have its values reflected in the database. Maintaining a single copy of each instance is a very valuable service provided by JDO's cache management architecture. It is not clear whether this is supported in the software being described by Bales.

Deleting the Instance

The final step in each program is the deletion of the Person instance. My JDO application no longer has a reference to the specific Person instance, so I have perform a query to access the instance. Once I have a reference to the Person instance, again, only a single line is necessary to delete the instance.

pm.deletePersistent(person);

When examining the two programs that delete the instance, another point of differentiation emerges. In Bales' program, it is not necessary to instantiate an instance of Person in the application; a SQL delete operation performs the necessary action. In my JDO application, it appears that I first need to query the database and read a Person instance into my application.

The underlying JDO implementation, however, may only need to instantiate the Person instance and initialize the identity of the instance. Unless the application explicitly accesses a field of the instance, the implementation does not need to read the object's state from the database into the instance in memory.

Assessment of Alternative APIs

Bales claims in his conclusion that using JDBC with an object-relational technology is far simpler than using JDO. I am sorry, I just don't see it. With his JDBC program, the application must deal with two distinct data models: the Java object model and the relational data model. There is a lot of statement setup code in the JDBC application program that is not necessary in the JDO program. I do not see how the JDBC application is simpler. Nor do I understand his claims that JDO is "flawed" and a "workaround."

Bales boasts about Oracle's implementation of OR technology, claiming you can "have your cake and eat it, too" by being able to use either a relational or object view of the information. I can use a JDO implementation with Oracle8i or Oracle9i and get JDO's object view of the database, and I can also use Oracle's underlying SQL facilities to access the same data. My JDO software is portable across all object and relational databases. But my direct use of Oracle-specific features will only work with Oracle. Multiple vendors provide support for JDO on Oracle8i.

There is one final, major point I would like to make. This example of persisting a Person object that has just six String fields is taken directly from the article written by Dion Almaer. This was done to show a very simple use of JDO. But this is an extremely simple and trivial example; it does not demonstrate the powerful capabilities provided by JDO. Most object models have many inter-relationships among classes, represented by references and collections that model one-to-one, one-to-many, and many-to-many relationships. JDO directly supports such object model relationships.

JDO also supports inheritance relationships among classes. This includes references (or collections of references) where the referred instances may be of various different subclasses of a common base class. This use of polymorphic references is very common in object models and provides the extensibility mechanism employed in many object-oriented programs. JDO supports such polymorphic references in the database, and also supports references to interfaces. The use of these features with JDO is no more complex than the use of the simple Strings found in this Person class.

I would like to see an example application that has the necessary JDBC code to support all of these modeling constructs. This will clearly demonstrate that JDO is simpler than JDBC for more realistic object models. Members of the JDO community have wanted someone to produce an article with such an example for quite some time to illustrate the differences between JDBC and JDO in supporting these more advanced modeling constructs. But so far, no one has been willing to produce it. Why? Because it would be a huge amount of work to get all of the cache management and object modeling capabilities (collections, inheritance, polymorphic references) implemented in JDBC. I assert that when you have such more realistic examples of Java object models, the JDO application will be substantially smaller and simpler than the corresponding JDBC.

Closing Remarks

To conclude, I would like to state that SQL is not bad; it does provide powerful relational capabilities that you will not find in the JDO API. It allows you to dynamically associate data via join operations and it supports projections, group-by operations, and many other capabilities useful in data analysis. But if you have Java object models or relationships among your classes, or if you use inheritance and other features supported by Java, JDO provides a very simple API to persist those object models in a wide variety of databases. I can run my JDO applications on top of a multi-user Oracle 8i or Oracle 9i database, taking advantage of its multi-user access capabilities. I can have my Java-centric view of the information with JDO. I can also go underneath the JDO API and directly use JDBC and SQL. In closing, Bales' JDO FUD is a dud.

References

David Jordan founded Object Identity, Inc. to provide Java Data Objects (JDO) consulting and training services. David is also a coauthor of O'Reilly's book on Java Data Objects, with Craig Russell.


Return to ONJava.com.

Copyright © 2009 O'Reilly Media, Inc.