JDO Unplugged

Farihah Noushene

Java Data Object (JDO) is an ‘Object Relational Mapping’ technology developed by Java Community Process (JCP), with active support from Sun MicroSystems. Craig Russell is the specification lead for the JDO expert group and David Jordan is an active member of that group.

Before the advent of JDO, we had various Sun's official standards for storing data like JDBC and EJB with CMP and BMP. Also, there are a number of ORM tools like Hibernate from SourceForge, Object Relational Bridge (OJB) from Apache, Toplink from Oracle, etc. In this article we shall unravel the unique features of JDO and how it differs from the rest.

When we use JDBC, we have to manage the values of the fields explicitly and map them into the relational database tables. Developers have to write their SQL code. Also, they have to implement their own mapping between the relational data model and their java object model, which is very complex. As such, the benefits of Object-Oriented development are lost.

In CMP, we can create new objects, modify them, delete them and view them in memory. A programmer need not write any SQL code for saving these objects in the corresponding database. The Container itself automatically does this. CMP supports both object databases and relational databases. However, CMP cannot be used for complex applications. Enterprise Java Beans or EJB is developed to support distributed object computing. Owing to the distributed capabilities, EJB applications become more complex. When our application needs object persistence but does not need distributed object capabilities, we can go in for JDO instead of EJB's CMP or BMP. JDO can run in an application server environment also and so the Session beans can be used along with JDO. Thus, it provides flexibility to choose the appropriate environment for our application. Also, JDO fully exploits the object-oriented capabilities of Java. OOPs techniques like Inheritance and polymorphism can be used in JDO, which is not possible in other technologies like JDBC and EJB architecture.

Unlike ORM tools like hibernate, which support only relational database, JDO supports a wide variety of data sources, including the data sources that are not commonly considered as databases. JDO supports Object databases and XML databases also. This is the major advantage of JDO over other ORM tools. Thus, JDO can be thought of as a futuristic approach.

JDO is an interface-based definition of object persistence and describes the storage, querying and retrieval of objects from datastores. The collection of all class definitions that form an application is called the application's 'object model'. These objects represent the state and behavior of the application. Storage of these objects beyond the lifetime of java virtual machine is called 'object-persistence'. Object Database Management Systems (ODBMS) are the storage environment for Objects. ODBMS suffers from various factors such as inefficiencies in query capability, lack of well-defined standards to invoke the persistent services, lock-in to the vendor’s product, etc.

JDO introduces a new step in the deployment process called 'Class Enhancement'. Each persistent class must be enhanced so that it can be used in JDO runtime environment. Persistent classes are compiled using our traditional java compiler to produce class files. The enhancer program reads these class files and JDO metadata to create new 'Enhanced Class Files'. Moreover, the enhanced class files are binary compatible and can thus be used with any JDO implementation. Enhanced class files are also independent of any specific datastore. The enhancement process does not alter the functional behavior of the class. It adds code to ensure that all the values of the field can be read from the datastore and modifications are tracked.

JDO even supports 'Persistence-by-reachability' (as in Hibernate). That is, JDO causes non-persistent instance of a persistence class to become persistent at commit if it is reachable by a persistence instance directly or indirectly. Let me elaborate. If we have two persistent capable classes that are related to each other, when we modify one of the class' instance it will be updated in the other table also.

JDO provides a query language called JDO Query Language (JDOQL) to access instance based on specific search criteria. JDOQL can be used in any type of database like Relational Database, Object Database, Hierarchical database, etc. JDO queries performed by 'Query' interface are used to select the instances that meet the specified criteria. The instance of 'Query' interface is created using 'PersistenceManager' interface. The JDO Query allows us to filter out instances from a set of instances specified by an Extent or a Collection. A Filter consists of a Boolean expression and it is applied to the instances. The query result includes all the instances for which the Boolean expressions are true.

The syntax is
Extent extent = manager.getExtent(player.class,true);
String filter = "name == a ";
Query query = manager.newQuery(extent,filter);

In filter, we can use a number of operators like equality operator (==), inequality operator (!=), comparison operators like greater than, greater than or equal to (<, >, <=, >=), Boolean operators like conditional AND, logical AND (&, &&, |, ||, !) and arithmetic operators like addition, subtraction, multiplication, division (+, -, *, %, ~). JDOQL also supports string expressions inside the filter. For this purpose, startsWith(..) and endsWith() methods are provided. For example, we can write the filter as name.startsWith("a%"). It is similar to our SQL command where we give "where name like 'a%'".

The order of the query result to be displayed is specified by an ordering statement. The ordering statement is a string that contains one or more ordering declarations, which consists of a java expression of orderable type followed by either 'ascending' or 'descending'. The syntax is
query.setOrdering("player.name ascending" + "player.game descending");

Finally, the guery is closed by the close(..) or closeAll() method. The 'close()' method closes the result returned by one call to 'query.execute()' method. The 'closeAll()' closes all the results from calls to 'query.execute()' method.

To quote from a recent article by Bruce Tate,
“Hibernate does not yet have the enterprise extensions or mapping flexibility of the best Java Data Object (JDO) solutions.”

JDO examples can be tested using Sun's JDO reference implementation. A number of other JDO implementations are also available. The JDO reference implementation can be downloaded from www.jcp.org and selecting JSR-12 or can be downloaded from sun java website. Go to http://www.java.sun.com/jdo/ index.html and download jdo-1_0_1-ri.zip. Extract the zip file to g:\jdo1. Inside g:\jdo1 we can find the following four jar files:

  1. jdo.jar: contains the standard interfaces and classes defined in the JDO specification;
  2. jdori.jar: is the Sun's reference implementation of JDO specification;
  3. btree.jar: is a software used by JDO reference implementation to manage the storage of data in a file; and
  4. jdori-enhancer.jar: contains the reference enhancer implementation. The classes of this jar file are also present in jdori.jar and so we do not need this jar. By using this jar, we can create JDO in any implementation and enhance classes using this enhancer also.

Also, there are three other jar files needed for our application. These include:

  1. jta.jar: The synchronization interface defined in package javax.transaction is used in JDO interface. It is present in this jar file. The jar file can be downloaded from http://www.java.sun.com/products/jta/index.html;
  2. antlr.jar: It is the parsing technology used in the implementation of JDO query language. It can be downloaded from http://www.antlr.org; and
  3. xerces.jar: Xerces-j is used to parse XML file and it can be downloaded from http://xml.apache.org/xerces-j/.

These three jars can be found in lib directory of hibernate (both 2 and 3) also.

First we will create a project directory called 'demojdo' in f drive.
f:\>md demojdo
f:\>cd demojdo
f:\demojdo>md jdopack

When we want to persist classes, package is essential in JDO. 'jdopack' is the package name given to our JavaBeans. Now set path and classpath as shown in code 1.

Code 1

f:\demojdo\jdopack>
set path=c:\windows\command;
g:\jdk1.4.2\bin

f:\demojdo\jdopack>
set classpath=f:\demojdo;
           f:\demojdo\jdopack;
f:\demojdo\jdo.jar;
f:\demojdo\jdori.jar;
f:\demojdo\btree.jar;
f:\demojdo\jta.jar;
f:\demojdo\xerces.jar;
f:\demojdo\antlr.jar;

Now we shall see how to create a datastore. First, we should create a property file as shown in code 2.

Code 2

//f:\demojdo\jdopack\jdo.properties

javax.jdo.PersistenceManagerFactoryClass=
com.sun.jdori.fostore.FOStorePMF
javax.jdo.option.ConnectionURL=fostore:dbdemo
javax.jdo.option.ConnectionUserName=farihah
javax.jdo.option.ConnectionPassword=
javax.jdo.option.Optimistic=false

The property file consists of various properties used by the JDO program during runtime. The 'PersistenceManager FactoryClass' property is used to specify the name of the implementation's class that implements the PersistenceManagerFactory interface. It specifies which JDO implementation we are using. The format of the 'ConnectionURL' property depends on the datastore used. 'dbdemo' refers to the datastore in our current project folder. It is possible to provide absolute path also. The other properties, 'ConnectionUserName' and 'ConnectionPassword', are necessary to establish connection to any datastore.

The JDO reference implementation has its own storage facility called 'File Object Store'(FOStore). We can create a new datastore by using FOStore and test our application. To create a datastore, edit the client file (refer code 3).

Code 3

//f:\demojdo\jdopack\jdocreatedb.java

import java.io.*;
import java.util.*;
import javax.jdo.*;

public class jdocreatedb
{
public static void main(String args[])
{
try
{
InputStream   fis = new FileInput
Stream("jdo.properties");

  Properties    props= new Properties();
props.load(fis);
props.put("com.sun.jdori.option.
ConnectionCreate","true");

  PersistenceManagerFactory    factory =
JDOHelper.getPersistence
ManagerFactory(props);

PersistenceManager       manager =
factory.getPersistenceManager();

  Transaction       tx =
manager.currentTransaction();
tx.begin();
tx.commit();
}
catch(Exception e1)
{ System.out.println(""+e1); }
}
}

When we compile and run the above program, we will get a FOStore database. It consists of two files dbdemo.btd and dbdemo.btx. The program creates a datastore using the jdo.properties file. 'com.sun.jdori.option.ConnectionCreate' is added to the property for creating a database. The 'PersistenceManagerFactory' provides property to control values that are used to establish a datastore connection. It is also responsible for creating and configuring the 'PersistenceManager' instance. 'JDOHelper' class provides methods to construct a 'PersistenceManagerFactory' instance from the properties object by using the method 'getPersistenceManagerFactory()'. The instance of 'PersistenceManager' is created from the 'PersistenceManager Factory' by using 'getPersistenceManager()' method.

To complete the datastore creation, we must begin and commit a transaction. The PersistenceManager's method, currentTransaction(), is used to create Transaction instance. The transaction consists of three methods: begin(), commit() and rollback(). To begin a transaction, begin() method is called. When we call commit(), all the changes are updated to the datastore and rollback() does not record the changes in the database.

We have now created a datastore 'dbdemo'. Next, we will see how to persist a class in it.

First, we have to create a class that is to be persisted. In JDO, to persist a class, package is a 'must'. Here, the package name is 'jdopack'. We will create a simple JavaBean as shown in code 4.

Code 4

//f:\demojdo\jdopack\player.java

package jdopack;

public class player
{
String  name;
String  game;

   public   player()  {  }

   public   player(String a,String b)
{
name = a;
game = b;
}
//----------------------------------
public String getName()
{
return name;
}
public void setName(String b)
{
name = b;
}
//-----------------------------------
public String getGame()
{
return  game;
}
public void setGame(String c)
{
game = c;
}
}

Next, we have to edit the metadata file. Metadata file is an XML format file and JDO uses this file to identify classes that need to be persisted and specify the persistence-related information that cannot be expressed in Java. The metadata file for a java package is named as 'package.jdo'. For our example, the metadata file is shown in code 5.

Code 5

//f:\demojdo\jdopack\package.jdo

<?xml version="1.0" encoding="UTF-8" ?>

<!DOCTYPE jdo PUBLIC
"-//Sun Microsystems, Inc.//DTD
Java Data Objects Metadata 1.0//EN"
"http://java.sun.com/dtd/jdo_1_0.dtd" >

<jdo>
<package name="jdopack">
<class name="player" />
</package>
</jdo>

 

Now we have to compile our JavaBean and enhance the class file. As already told, for the java class to be persisted, the class must be enhanced. JDO provides a reference enhancer that reads the class file produced by the java compiler and the metadata file to produce the 'enhanced class' file. The command for enhancement in our reference enhancer is shown below,

F:\demojdo\jdopack>javac player.java
(We will get the class file.)

F:\demojdo\jdopack>
java com.sun.jdori.enhancer.Main\demojdo/jdopack/package.jdo\demojdo/
jdopack/player.class
(Type in a single line)

After the command gets executed, we will get the output as 'done'. This command creates a folder in the name of our package i.e. 'jdopack' and places the enhanced class file in that folder. The full path of our enhanced class file is 'f:\demojdo\jdopack\jdopack'. Copy it to our project folder f:\demojdo\jdopack so that it is accessible to our client program. We no longer need our ordinary class file.

We will now us see how to add, delete, find and update records in our datastore 'dbdemo' and how to persist data in it.

To add a new record we must first get an instance of 'PersitenceManager' and begin the Transaction as shown above. After that, records can be added using the player bean's constructor with arguments as:
player player1 = new player(a,b);
where a and b represent name and game. Alternatively, we can also use set methods and no argument constructor as:
player player1 = new player();
player1.setName(a);
player1.setGame(b);

To make the player instance to be persistent, we must call 'makePersistence(..)' method of 'PersitenceManager' before committing the transaction(full code is being given shortly).

To find a record, i.e. to access the instance in our datastore, we can iterate an extent or execute a query.

An extent is a facility used to access all the instance of the class. When we want to show all the instances, we can just iterate an extent. The 'getExtent(..)' method of 'Extent' class is used to give class name. After that, 'Iterator' class is used to get all the records as shown in code 6.

Code 6

Extent extent = manager.getExtent(player.class,true);
Iterator iter = extent.iterator();
while(iter.hasNext())
{
Object  ob = (Object)iter.next();
System.out.println(ob.getName());
System.out.println(ob.getGame());
}


When we want to find a particular record, we can use extent facility to get all the records and filter them by executing a query. The JDO 'Query' interface is used to select instances that meet the specified criteria. The instance of 'Query' interface is created using 'newQuery(..)' method defined in 'PersistenceManager' interface. The Query is initialized with an extent and a query filter to apply to that extent. The syntax is given in code 7.

Code 7

Extent  extent =  manager.getExtent(player.class,true);
String  filter =  "name == a";
Query   query  =  manager.newQuery(extent,filter);
query.declareParameters("String a");

Collection  list1  =  (Collection)query.execute(a);
Iterator    i  = list1.iterator();
while(i.hasNext())
{
Object   ob = (player)i.next();
System.out.println(ob.getName());
System.out.println(ob.getGame());                 
}
query.close(list1);

The 'name' identifier in the filter indicates the name field of our class. The filter expression requires the name field of the class equal to the given variable 'a'. Here we can use '==' operator directly to compare two strings.  After that, the query is executed using the method execute(). The query parameter provides a value to be used when a query is executed. The query is executed by using the command 'query.execute()' and the method returns the result. The return type of the method is declared as Object. The returned instance is always a Collection and hence we cast the query result as Collection. Then we use 'Iterator' to get each individual objects as shown above. Finally, the query is closed using 'close(..)' method

When we remove all the references to a persistence instance, the instance will not be automatically deleted. We have to explicitly delete individual instances. To delete an instance, we have to get the reference of that instance as shown above and delete it. We can call 'deletePersistence(..)' method of 'PersitenceManager' to delete that instance.

To update records, we must first get an instance of 'PersitenceManager' and begin the Transaction as usual. After that instance is located, as shown above (to find the record), we can use set methods and update the properties as shown below: 
player1.setName(a);
manager.makePersistent(player1); 

To make the player instance to be persistent, we must call 'makePersistence(..)' method of 'PersitenceManager' before committing the transaction.

The complete console program is as in code 8.

Code 8

f:\demojdo\jdopack>

package jdopack;

import java.io.*;
import java.util.*;
import javax.jdo.*;
import javax.jdo.spi.*;

public class jdoclientConsole
{
public static void main(String args[])
{

 PersistenceManagerFactory  factory=null;
 PersistenceManager         manager=null;
Transaction                tx;
String s = "";

      try
{
InputStream     fis = new
FileInputStream("jdo.properties");
Properties    props = new Properties();
props.load(fis);

         factory = JDOHelper.getPersistence
ManagerFactory(props);
manager =  factory.
getPersistenceManager();       
}   catch(Exception e1)
{ System.out.println(""+e1); }

      try
{
do
{
System.out.println
("Add/ Delete/  Showall/ Find/ Update");
 DataInputStream   ins = new
DataInputStream(System.in);
s = ins.readLine();

            if(s.equals("add"))
{
tx = manager.
currentTransaction();
tx.begin();  

               System.out.println
("Enter Name:");
String a = ins.readLine();

               System.out.println
("Enter Game:");
String b = ins.readLine();

    player    player1  =  new player(a,b);
     manager.makePersistent(player1);
tx.commit();
}

if(s.equals("delete"))
{
tx = manager.current
Transaction();
tx.begin();  

               System.out.println
("Enter Name:");
String a = ins.readLine();

     Extent     extent = manager.getExtent
(player.class,true);
Query      query = manager.newQuery
(extent,"name == a");
query.declareParameters
("String a");
Collection    list1 = (Collection)
query.execute(a);

     Iterator      i = list1.iterator();
player player1 = null;

               while(i.hasNext())
{
player1= (player)i.next();
                 manager.deletePersistent
                                (player1);
}
query.close(list1);                                       
tx.commit();
}

if(s.equals("find"))
{
tx = manager.current
Transaction();
tx.begin();  

     System.out.println("Enter Name:");
String a = ins.readLine();

     Extent    extent  =  manager.
getExtent(player.class,true);

     Query     query   =  manager.
newQuery(extent,"name == a");
query.declareParameters("String a");

     Collection   list1 = (Collection)
query.execute(a);

     Iterator     i  =  list1.iterator();
player player1 = null;
while(i.hasNext())
{
player1= (player)i.next();
System.out.println
(player1.getName()+
".."+player1.getGame());
}
query.close(list1);
tx.commit();
}

if(s.equals("update"))
{
tx = manager.current
Transaction();
tx.begin();  

               System.out.println
("Change Name or Game");
String c = ins.readLine();

               if(c.equals("name"))
{

     System.out.println("Enter Game :");
String d = ins.readLine();

System.out.println("What is the new
Name?");
String e = ins.readLine();

     Extent  extent = manager.getExtent
(player.class,true);
Query   query  = manager.newQuery
(extent,"game == d");
query.declareParameters("String d");
Collection   list1 = (Collection)
query.execute(d);

     Iterator      i = list1.iterator();
player player1 = null;

while(i.hasNext())
{
 player1 = (player)i.next();
            player1.setName(e);
 manager.makePersistent(player1);
}
query.close(list1);

               }                 
if(c.equals("game"))
{

     System.out.println("Enter Name :");
String d = ins.readLine();
System.out.println
("What is the new Game?");
String e = ins.readLine();

     Extent   extent = manager.getExtent
(player.class,true);
Query    query  = manager.newQuery
(extent,"name == d");
query.declareParameters("String d");
Collection   list1 = (Collection)
query.execute(d);

     Iterator i = list1.iterator();
player player1 = null;

while(i.hasNext())
{
  player1 = (player)i.next();
            player1.setGame(e);
            manager.makePersistent(player1);
}
query.close(list1);

}                        
tx.commit();
}

if(s.equals("showall"))
{
tx = manager.
currentTransaction();
tx.begin();  

     Extent   extent = manager.getExtent
(player.class,true);
Iterator    it  = extent.iterator();
while(it.hasNext())
{
player player2 = (player)
it.next();
System.out.println
(player2.getName()+"---"
+player2.getGame());
}
tx.commit();
}
}while(!s.equals("over"));
}
catch(Exception e1)
{ System.out.println(""+e1); }

   }
}

We can use the enhanced class itself to compile and run the client program. This completes our two-part tutorial on JDO.

The author can be contacted at: farihahnoushene@yahoo.co.in.








}