JDO (Java Data Objects) is an effort by Sun to provide a standard interface for transparent object persistence.

"Transparent" refers to the fact that the JDO implementation manages the persistence of selected classes without these classes needing to be aware of how their persistent state is recorded. Of course, parts of the application will need to be aware which classes are persistent, as they will need to read and write them to the data store. JDO defines an API (in the javax.jdo namespace) for interacting with persistence capable classes.

At the time of writing (October 2001), the JDO spec was at the "Proposed Final Draft" (version 0.95) stage and two variants of the JDO reference implementation (RI) were under development by Sun.

JDO does not dictate any particular implementation for the underlying data store and this is reflected in the two RI variants, one of which uses the file system as the data store while the other uses JDBC with a RDBMS.

DB Visual Architect - A tool can generare/reverse the JDO and Database by ERD and Class Diagram

A brief description of using JDO follows.

--Martin


Making a class persistence capable#

The PersistenceCapable interface#

To be persisted to the data store, a class must be persistence capable. This means that the class must implement the javax.jdo.PersistenceCapable interface. In practice, JDO does not suggest that programmers manually implement the PersistenceCapable interface as this would be time consuming and error prone. Instead, it expects that any JDO implementation will provide tools for implementing the interface in an automated way: either through source code generation or post-processing of class files (enhancement).

Mapping a persistence capable class to the data store#

The mapping between each persistence capable class and the data store is defined in an XML metadata file. This file can be used by a JDO implementation during the class enhancement process and at runtime to manage the persistence capable classes.

Because the JDO specification is independent of the underlying data store, a JDO implementation can use vendor specific mapping extensions to customise the metadata for a specific data store. For example, a RDBMS based JDO implementation would include extensions to map class names to table names and class attribute names to column names.

The JDO API#

PersistenceManager#

The PersistenceManager is the main access point for JDO programming. Amongst others, it provides methods for:
  • Obtaining an Extent
  • Creating a Query
  • Obtaining a Transaction
  • Creating and deleting PersistenceCapable objects in the data store
A PersistenceManager itself is obtained from a PersistenceManagerFactory. To create an instance of the PersistenceManagerFactory, you must instantiate the actual implementation-specific class.

Extent#

An Extent represents the set of all instances of a given class in the data store. An Extent can be used to iterate over all persistent instances of a class, or as the basis for a query.

An Extent is created using the PersistenceManager's getExtent() method.

Query#

A Query is used to retrieve a Collection of PersistenceCapable objects from the data store.

A Query is created using one of the PersistenceManager's overloaded newQuery() methods.

Queries are loosely modelled on the structure of a Java method, having:

  • A class, representing the class of objects that the query is acting on (and therefore the class of objects returned by the query).
  • Optional parameters to the query (akin to a method's arguments).
  • Optional variables (effectively local variables of the method).
  • A filter (to restrict the instances returned by the query) and an ordering specification (to order the results of the query). These two properties allegedly represent the body of the method.
Where parameters and variables are of types belonging to another package, the Query even supports the idea of import statements.

This is probably best demonstrated with an example. Consider an employee database where employees are represented by the Employee class:

public class Employee
{
    private int employeeNumber;
    private String firstName;
    private String surname;
    // --- Getters & setters omitted for brevity ---
}

and this method from the application:

/**
 * This method returns a Collection of all the employees
 * with a surname beginning with the passed search string.
 * Matching employees are ordered by surname and first name.
 */
public Collection findEmployees(String searchName)
{
    // Lets consider all employees.
    Extent myExtent = myPersistenceManager.getExtent(Employee.class, true);

    // Create a query to retrieve some employees.
    Query myQuery = myPersistenceManager.newQuery(Employee.class, myExtent);

    // Only retrieve employees with a matching surname.
    myQuery.declareParameters("String aName");
    myQuery.setFilter("surname.startsWith(aName)");

    // Order the results by surname and first name.
    myQuery.setOrdering("surname ascending, firstName ascending");

    // Execute the query binding the search key to the "aName" parameter.
    Collection employees = (Collection)myQuery.execute(searchName);
    myQuery.close();

    return employees;
}

Creating, updating and deleting objects#

The creation, update or deletion of PersistenceCapable objects must be done within the scope of a Transaction (see below). For brevity, transactions are not shown in the following examples.

Adding a new object to the data store#

JDO supports persistence by reachability, so given one PersistenceCapable object that exists in the data store, another PersistenceCapable object can be added to the data store by associating it with the first object.

Continuing with the employee database example, consider a PersistenceCapable Department class that contains a Collection of Employee objects:

public class Department
{
    private String name;
    private Collection employees;
}

If the Department object already exists in the data store (perhaps retrieved using a Query), a new Employee can be added to the data store simply by adding it to the Department's Collection:

    someDepartment.getEmployees().add(newEmployee);

To create a new, unconnected, object in the data store (say a new Department), use:

    myPersistenceManager.makePersistent(newDepartment);

Modifying an existing persistent object#

A PersistenceCapable object that already exists in the data store is automatically updated in the data store if any of its attributes are updated.

Deleting an object from the data store#

A PersistenceCapable object can be removed from the data store using:
 
    myPersistenceManager.deletePersistent(someEmployee);

Transaction#

As mentioned above, all modifications to the data store must be done within the scope of a Transaction. Modifications are only made to the data store once the Transaction is committed:
 
    Transaction t = myPersistenceManager.currentTransaction();
    t.begin();
    myPersistenceManager.makePersistent(newDepartment);
    t.commit();

Modifications could have been rolled back using t.rollback().

Further reading#

See the JDO Home Page for further information.
Oh, this is all fine and dandy and I love the basic concept behind it, but is this really needed?

We've got JDBC for (relational) database access, that works great, is mature and stable and pretty easy to use.

We've got entity beans which are just dataobjects representing database rows. With EJB 2.0's CMP-beans the server does most of the dirty work behind the scenes, you just have to specify the mapping between fields in the deployment descriptor and optional query parameters in a sql-like (actually a subset of) query language.

Now we've got JDO, which seems to be sort of mix between these two. There's another query language (represented by the Query objects) which seems to be a mix between SQL and regular Java code. And from what I've read on the subject it looks like using it is unnecessary complicated.

What I like about JDO is the implementation transparency, since the first two methods are obviously very (R)DBMS-centric and sometimes a flat file is all you need.

-- raisanen@regex.fi


TheServerSide.com has an interesting discussion on JDO vs Entity Beans.

It's also worth noting that unlike EJBs, JDO applications don't need to run in a managed environment - indeed Sun maintain that JDO is suitable for everything from embedded systems to J2EE app servers.

I find that using raw JDBC for anything more than trivial applications can be a significant development overhead, particularly if trying to map a moderately complex domain object model to a relational database. I don't see JDO as revolutionary in this respect - there have been object/relational mapping tools around for years to do this kind of thing - but having a standardised interface (and free implementation :-)) should bring this approach further into the mainstream.

Although some aspects of the Query interface are a little peculiar, the actual query language itself is quite straightforward.

For a filter, the syntax is simply a subset of Java expression syntax and supports the usual Java operators: ==, !=, >, <, >=, <=, &&, ||, etc. Method invocations aren't allowed though, with the exception of:

  • String.startsWith()
  • String.endsWith()
  • Collection.isEmpty()
  • Collection.contains()

It gets (much) less intuitive once you start dealing with collections, and that's where Query.declareVariables() comes in. Using the classes in the above examples, say you wanted to find all Departments with an Employee named "Martin":

 
    Extent myExtent = myPersistenceManager.getExtent(Department.class, true);
    Query myQuery = myExtent.newQuery(Department.class, myExtent);

    // Declare a "local" variable to represent an employee.
    myQuery.declareVariables("Employee emp");

    // The following filter has the effect of testing the right hand side
    // (and it must be the right hand side!) of the expression against each
    // object contained in the employees collection for each Department in
    // turn.  Departments that do not contain an Employee that satisfies the
    // expression are excluded from the query results.

    myQuery.setFilter("employees.contains(emp) && emp.firstName == \"Martin\"");

    Collection employees = (Collection)myQuery.execute();

A similar example and the full query syntax can be found in the JDO spec, available via the JDO Home Page.

--Martin

Add new attachment

Only authorized users are allowed to upload new attachments.
« This page (revision-22) was last changed on 20-Dec-2006 13:20 by 213.170.81.122