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


Don Bales

Flawed JDO Points the Way to the "Objectbase"

by Donald Bales, author of Java Programming with Oracle JDBC
04/24/2002

There's been a lot of hype lately about Java Data Objects (JDO). It appears to be the new silver bullet that will alleviate all of our coding drudgery. 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. To be fair however, JDO does have it merits.

My own take on why the specification evolved is simple: I too am tired of writing JavaBeans to present data from a relational database as objects in my Java programs. I've written lightweight JavaBeans, that simply hold data like a structure and have accessor and mutator methods, to heavyweight JavaBeans, that know how to retrieve and persist themselves.

I have two issues with JDO. The first is that somehow SQL has become a bad thing. SQL remains one of the major breakthroughs in our industry. Its non-procedural mechanics freed us from the high priests that used to control persisted data, and its power to bring together formerly unrelated populations of information still make it the most powerful computing tool of our time. The second is that JDO's abstraction treats a Database Management System (DBMS) like a file. Restated, instead of treating a database like an extension of memory for your application, you end up treating it like a file in a file system, where you open the file, read it into your application, write any changes back out to disk, and then close the file. This kind of behavior does not properly facilitate multi-user access.

Most importantly though, is that a workaround like JDO is indicative of a more fundamental problem: as object-oriented programmers, we have been leveraging object-orientation to improve how we build software, but we have not applied it to solving the business problems for which we write software. We are, for lack of a better term, stuck in the world of functional decomposition. Rather than view the world in a natural way, we abstract things in the real world by categorizing information about them, but ignore their behaviors.

You can see evidence of this predominant way of thinking in the way we communicate. Take the word database for example. Our everyday language is imbued with this word. Object and object-relational persistence vendors call their solutions object database management systems, and object-relational database management systems. Even the employees at Oracle call their persistence solution an object-relational database. What we really need is a different term: objectbase. We need to move our thinking and our problem domain object models into an object persistence layer: an objectbase.

JDO itself is a sentinel that we need to make the paradigm shift from a database -- a place where we persist information -- to an objectbase -- a place where we persist information and behavior. I'm fortunate to have lived through one such paradigm shift, from ISAM/VSAM files, hierarchal databases, and network databases to relational databases. So for me, the shift in thought is not so difficult. But what is an objectbase?

To some, an objectbase is a place where you store and retrieve binary executables that can be accessed as needed anywhere at any time. Computers and networks will be extremely different than they are today when that definition of an objectbase is fulfilled. I, just like you, need a solution now. And that solution currently exists in the form of object-relational (OR) technology. With OR, you define object types in the objectbase, use a tool to generate your JavaBeans to mirror the objectbase types, and then use the Java Database Connectivity (JDBC) API, or SQLJ, to access objects from Java. Oracle's implementation of OR provides you with all of the strengths of their relational database, plus the ability to add methods to object types (or user defined types (UDTs), as they are called in SQL:1999). In this article, in an effort to give you a fair comparison of what is necessary to use OR with JDBC vs. JDO, I'll be showing you how to insert, update, and select a person object using JDBC in a similar fashion to "Using Java Data Objects," published recently on OnJava.com.

Creating a User Defined Type

The first step is to create a user defined type (UDT). To do so, you'll use the SQL CREATE TYPE command, as in this example:

create type PERSON_TYPE as object (
name        varchar2(255),
address     varchar2(255),
ssn         varchar2(255),
email       varchar2(255),
home_phone  varchar2(255),
work_phone  varchar2(255) )

The primary data types you have available to use in defining a new type are those supported by your OBMS vendor. For Oracle, the three most common primary types are: DATE for date and time values, NUMBER for numeric values, and VARCHAR2 for textual values. Here, I've defined a new type, named PERSON_TYPE.

Creating an Object Table

The next step is to create a table based upon our new UDT. A table based upon a UDT is called an object table. To create an object table, you'll use the SQL CREATE TABLE command, as in this example:

create table PERSON_OBJECT_TABLE
          of PERSON_TYPE

Here, I've created an object table named PERSON_OBJECT_TABLE based upon the UDT PERSON_TYPE. This is the table I'll be working with in the example demonstration program. It's important to note that for this example, I've decided to use the object references created by the objectbase as a unique identifier for my object relationships. As an alternative, I could have just as easily used primary and foreign keys, and constraints, as I would with a relational database.

Creating a Mirror Java Class

Once a UDT is created in your database, you can create a mirror class for your Java program by coding a JavaBean that implements the SQLData interface, or, if you're using Oracle, you can use the JPublisher Utility to generate a JavaBean for you. Example 1 is a hand-coded class, Person, to mirror the objectbase's UDT PERSON_TYPE.

Class Person consists of:


Example 1. The Person class

import java.io.*;
import java.sql.*;

/**
A mirror class to hold a copy of SCOTT.PERSON_TYPE
*/
public class Person implements SQLData, Serializable {
  private String = name;
  private String = address;
  private String = ssn;
  private String = email;
  private String = home_phone;
  private String = work_phone;

  public Person() {
  }
  // SQLData interface
  public String getSQLTypeName()
   throws SQLException {
    return "SCOTT.PERSON_TYPE";
  }
  public void readSQL(SQLInput stream, String type)
   throws SQLException {
    name        = stream.readString();
    address     = stream.readString();
    ssn         = stream.readString();
    email       = stream.readString();
    home_phone  = stream.readString();
    work_phone  = stream.readString();
  }
  public void writeSQL(SQLOutput stream)
   throws SQLException {
    stream.writeString(name);
    stream.writeString(address);
    stream.writeString(ssn);
    stream.writeString(email);
    stream.writeString(home_phone);
    stream.writeString(work_phone);
  }
  // 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;
  }
}

Coding a JavaBean like this is not hard, but it is tedious. The readSQL() and writeSQL() methods simply read and write the data values from or onto their respective streams in the order in which the attributes exist in the UDT.

Java Programming with Oracle JDBC

Related Reading

Java Programming with Oracle JDBC
By Donald Bales

Add The New Object Type to the JDBC Type Map

Before you can use your mirror class to retrieve objects from, and to save objects to, an objectbase, you need to let the JDBC Connection you'll be using know that a mirror class exists, and to which UDT it should be mapped. To accomplish this, you'll update the connection's type map.

The type of Connection object, whether it comes from DriverManager or a DataSource, is of no consequence; either will work. In the demonstration program from Example 2, DemonstrateOR, I use a Connection object returned from DriverManager. First, the program gets the current type map from the connection by calling its getTypeMap() method. Next, the program adds an entry to the Map object using its put() method, passing the name of the UDT, and a copy of the mirror class, Person. Last, the program stores the updated type map in the connection by calling its setTypeMap() method, passing the updated Map object. At this point, the JDBC driver knows what Java class to instantiate when a copy of the UDT is retrieved from the objectbase, and vice versa.

Inserting a New Instance in the Objectbase

Inserting a Person object into the objectbase is now a simple matter of using a properly-formatted SQL INSERT statement and the same JDBC code that you would typically use to insert an entry into a relational database. In Example 2, the program starts out by creating a new instance of a Person object by using the new operator. Next, it sets the attribute values, using the appropriate setXXX() methods. The program proceeds by creating a prepared statement based upon a SQL INSERT statement that has one argument in its VALUES clause.

Next, the program calls the prepared statement's setObject() method, passing it the Person object. The JDBC driver uses the information the program added to the typemap to map the Person object to the PERSON_TYPE UDT. Then, the program executes the prepared statement using the executeUpdate() method, which returns the number of rows inserted. It then performs the necessary cleanup by closing the prepared statement and committing the transaction. At this point, a new PERSON_TYPE object exists in the PERSON_OBJECT_TABLE.


Example 2. A JDBC object-relational demonstration program

import java.io.*;
import java.math.*;
import java.sql.*;
import java.text.*;
import oracle.jdbc.driver.*;

/**
A program to demonstrate Oracle object-relational technology
*/
public class DemonstrateOR {
  Connection conn;

  public DemonstrateOR() {
    try {
      DriverManager.registerDriver(
        new oracle.jdbc.driver.OracleDriver());
      conn = DriverManager.getConnection(
        "jdbc:oracle:thin:@dssw2k01:1521:orcl",
        "scott",
        "tiger");
      conn.setAutoCommit(false);
    }
    catch (SQLException e) {
      System.err.println(e.getMessage());
      e.printStackTrace();
      System.exit(1);
    }
  }

  public static void main(String[] args)
   throws Exception {
    new DemonstrateOR().process();
  }

  public void process() throws SQLException {
    PreparedStatement pstmt =  null;
    ResultSet         rslt  = null;
    Statement         stmt  = null;
    Person            person = null;

    // Update the connection's type map
    try {
      java.util.Map map  conn.getTypeMap();
      map.put("SCOTT.PERSON_TYPE",
       Class.forName("Person"));
      conn.setTypeMap(map);
    }
    catch (ClassNotFoundException e) {
      System.err.println("Class Not Found Error: " + e.getMessage());
      System.exit(1);
    }
    catch (SQLException e) {
      System.err.println("SQL Error: " + e.getMessage());
      System.exit(1);
    }

    // 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");

      pstmt = conn.prepareStatement(
        "insert into PERSON_OBJECT_TABLE values ( ? )");
      pstmt.setObject(1, person);
      int rows = pstmt.executeUpdate();
      pstmt.close();
      pstmt = null;
      System.out.println(rows + " row inserted");
      conn.commit();
    }
    catch (SQLException e) {
      System.err.println("SQL Error: " + e.getMessage());
      System.exit(1);
    }
    finally {
      if (pstmt !=null)
        try { pstmt.close(); } catch (SQLException ignore) { }
    }

    // Update the object using standard JDBC
    Ref personRef = null;
    try {
      stmt = conn.createStatement();
      rslt = stmt.executeQuery(
       "select ref(p), " +
       "       value(p) " +
       "from   PERSON_OBJECT_TABLE p " +
       "where  name = 'Bales, Donald J.'");
      rslt.next();
      personRef = (Ref)   rslt.getObject(1);
      person = (Person)rslt.getObject(2);
      rslt.close();
      rslt = null;
      stmt.close();
      stmt = null;

      // Let's change my email address
      person.setEmail("don@donaldbales.com");

      pstmt = conn.prepareStatement(
       "update PERSON_OBJECT_TABLE p " +
       "set    value(p)=? " +
       "where  ref(p)=?");
      pstmt.setObject(1, person);
      pstmt.setRef(2, personRef);
      int rows = pstmt.executeUpdate();
      pstmt.close();
      pstmt = null;
      System.out.println(rows + " row updated");
      conn.commit();
    }
    catch (SQLException e) {
      System.err.println("SQL Error: " + e.getMessage());
      System.exit(1);
    }
    finally {
      if (rslt  !=null)
        try { rslt.close();  } catch (SQLException ignore) { }
      if (stmt  !=null)
        try { stmt.close();  } catch (SQLException ignore) { }
      if (pstmt !=null)
        try { pstmt.close(); } catch (SQLException ignore) { }
    }


    try {
      // Create and execute the object sql statement
      stmt = conn.createStatement();
      rslt = stmt.executeQuery(
        "select value(p) from PERSON_OBJECT_TABLE p");
      while (rslt.next()) {
        // Cast the object
        person = (Person)rslt.getObject(1);
        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());
      }
      rslt.close();
      rslt = null;
      stmt.close();
      stmt  = null;
    }
    catch (SQLException e) {
      System.err.println("SQL Error: " + e.getMessage());
      System.exit(1);
    }
    finally {
      if (rslt !=null)
        try { rslt.close();  } catch (SQLException ignore) { }
      if (stmt !=null)
        try { stmt.close();  } catch (SQLException ignore) { }
    }

    // Delete the person
    try {
      stmt  = conn.createStatement();
      int rows = stmt.executeUpdate(
        "delete PERSON_OBJECT_TABLE " +
        "where  ssn = '999-99-9999'");
      stmt.close();
      stmt = null;
      System.out.println(rows + " row deleted");
      conn.commit();
    }
    catch (SQLException e) {
      System.err.println("SQL Error: " + e.getMessage());
      System.exit(1);
    }
    finally {
      if (stmt != null)
        try { stmt.close(); } catch (SQLException ignore) { }
    }

  }

  protected void finalize()
   throws Throwable {
    if (conn != null)
      try { conn.close(); } catch (SQLException ignore) { }
    super.finalize();
  }
}

Updating an Instance from the Objectbase

The next step in the demonstration is to update the Person object the program just stored in the objectbase. To do this, it retrieves the object's reference and an instance of the object. To accomplish this, it utilizes the objectbase's functions ref() and value() in the SQL SELECT statement. I could have just used unique information about the object's attributes to update it (the reference is not necessary), but I wanted to show that a reference is similar in concept to one in Java, in that it is a unique identifier for an object.

First, the program creates a Statement object. Next, it retrieves a ResultSet object by executing the SQL SELECT statement. It forwards to the first row of the result set by calling the next() method. Then, it stores the REF object in a Ref variable by calling getObject() and casting the returned Object to a Ref. Next, it performs a similar call for the Person object. Now that the program has a copy of the Person object, I change my email address and proceed to save it to the objectbase. To do so, the program starts out by creating a properly-formatted SQL UPDATE statement that once again utilizes the value() and ref() objectbase functions. Next, it calls the prepared statement's setObject() method twice; the first time to set the Person object, and the second time to set its reference. Then, the program tidies things up by closing the prepared statement and committing the transaction. Now the objectbase contains an updated entry with my new email address.

Selecting an Instance from the Objectbase

Retrieving an object is straightforward. If you were paying close attention, you'll have realized that the program just did it when it updated the Person object. This time, the program retrieves the Person object and then echoes its attributes to the screen. Proof that the program actually works. Wow.

Deleting an Instance from the Objectbase

Finally, the demonstration program executes a basic SQL DELETE statement, using JDBC, to delete the Person object from the objectbase. By doing so, the program cleans up after itself, so you can try the program over and over again to your heart's content.

Transforming a Relational Database using Object Views

As you have just seen in the demonstration program, using object-relational technology with JDBC is fairly simple and uncomplicated. The real beauty behind Oracle's implementation of OR technology is that you can have your cake and eat it too. You can use Oracle8i or Oracle9i as an objectbase and then use the SQL TABLE operator to create relational views for your non-object-oriented development or third-party software tools. Or you can put an object face on a legacy relational database by creating object views, so you can access it with your OOD tools. This means that you don't have to wait for a new project to start leveraging OR technology; you can start using it immediately on any Oracle8i or Oracle9i server.

Conclusion

As you can see from Example 2, using JDBC with an object-relational technology is far simpler than using JDO. You can continue to leverage the strengths of SQL and at the same time migrate an existing relational database to an objectbase. Yet, the most important point of using OR technology is that the object model exists in the objectbase, not just in a set of Java programs. This means that the object model can be leveraged by any object-oriented programming language or OR-capable tool. With Oracle, it also means that you can continue to use your favorite relational tools as well.

Because I've provided a somewhat apples-to-apples comparison of JDBC and JDO, the example program does not completely demonstrate the capabilities of JDBC with OR technology. For example, it does not show the use of arrays (or collections, as they are also called) or methods. I do, however, cover these topics in-depth in my book Java Programming with Oracle JDBC. I urge you to bypass the extraneous layers of abstraction that JDO and similar object/relational mapping tools create, and address the fundamental problem: you need to use an objectbase for your object-oriented software solutions. I look forward to your feedback.

Donald Bales is a Systems Architect, Computer Applications Consultant, and Business Analyst specializing in the analysis, design, and programming of web-based, client-server, and distributed applications, the internationalization of existing applications, systems integration, and data warehousing.


Return to ONJava.com.

Copyright © 2009 O'Reilly Media, Inc.