JDO

The last persistence technology that you will be looking at, Java Data Objects (JDO), aims high and it aims low. That is, it is intended both for use within embedded devices (J2ME) but also for use within J2EE environments. In a J2EE environment, JDO can either supplement or supplant Entity EJBs; if supplementing Entity EJBs, the application of JDO may be hidden if CMP is being employed (that is, the EJB vendor uses JDO but the bean provider is unaware of this), or it can be used by the bean provider directly if BMP is in use.

JDO is the most recent of the Java persistent technologies and, at the time of writing, its specification was still in draft with only an incomplete reference implementation. Moreover, as a technology it seems to be running in parallel with the various Java platform editions (J2ME, J2SE, and J2EE), and there appear to be no clear indications within which platform it will eventually reside. Nevertheless, JDO has raised some significant interest as an API, especially for vendors of OODBMS and O/R mapping tools. Indeed, the JDO expert group includes representatives from Versant, Poet, Object Design, and Gemstone, among others. Because JDO completely hides the details of the data store internals, it is also suitable to access ERP systems; some ERP vendors (such as SAP) have committed to adopting it.

Tip

One way to think of JDO is as a vendor-neutral Java API to OODBMS and O/R mapping tools, just as JDBC provides a vendor-neutral Java API to RDBMS.


The overriding objective of JDO is to provide persistence transparency. In other words, the objects that are to be persisted—so-called persistent-capable objects—do not need to include any logic to make them persistent. Contrast this with EJB Entity beans that need to implement ejbLoad() and ejbStore(), or with EJB Session beans (and for that matter servlets) that require reams of JDBC or SQLj calls. The only classes that need to use the JDO interfaces and classes are those that manage the lifecycle of persistent objects.

As an example of transparent persistence, consider that persistent-capable objects can also have references to other objects. Assuming that the field that holds this reference has been marked as being persistent, these referenced classes will then also be made persistent. This is sometimes called “persistence by reachability.” Again, contrast this to EJB, where a relationship between beans required complex configuration in the EJB deployment descriptor, moreover being constrained to relate through the EJB's local interface.

Note

It is perhaps a little misleading to claim that JDO does not need relationships between classes to be defined. Rather, it is not within the scope of JDO to be concerned about this. A JDO implementation provided by an OODBMS vendor will work in a different way than one provided by an O/R mapping tool vendor. Any mapping or other configuration information that might need to be done is performed entirely with vendor-specific tools.

Earlier, JDO was compared to JDBC. JDBC provides a standard API, but leaves RDBMS vendors at liberty to implement their own data store and network protocols. Equally, JDO provides just an API and does not get involved in the internals. This makes JDO applications portable across JDO implementations, but obviously requires any vendor-specific configuration to be re-applied.


JDO Concepts

JDO's approach to object persistence revolves around the concept of a cache. This cache belongs to a client, rather than the server; the objects in the cache are not shared among all clients. In a J2EE environment, a stateful Session bean would usually be a client; each active Session bean would have its own cache. At any given time, the cache that holds the objects is associated with at most one connection and at most one transaction.

Over time, that cache can be used with different transactions, and, for that matter, with different connections.

The JDO interface that controls the client's cache is javax.jdo.PersistenceManager. In a J2EE environment, an instance of this interface is obtained from a javax.jdo.PersistenceManagerFactory that, in turn, is obtained via JNDI. The JDO specification describes how the J2EE Connector architecture is used to actually configure a PersistenceManagerFactory into JNDI; you'll be learning something about this on Day 19, “Integrating with External Resources.” All you need to appreciate for now is that a JDO vendor (an OODBMS vendor, O/R mapping tool vendor, or ERP vendor) will have implemented the appropriate J2EE Connector interfaces such that a PersistenceManagerFactory will be available for you to look up.

Note

JDO also integrates with XA, meaning that distributed transactions across multiple JDO implementations, and indeed RDBMS data stores, are supported.


JDO defines two main ways in which the cache can be used. All JDO implementations must support so-called data store transactions and can optionally support optimistic transactions:

  • When a persistent-capable object is in a cache through a data store transaction, it effectively prevents any other user from holding this object in his or her cache.

    It is almost as if the object is “checked out” to the user's cache, a little like checking out code from a source code control system. It remains there for that user's exclusive use (to be read or modified) until he or she indicates that the transaction is complete. Any other user who wants to use the object must wait until the original user's transaction has completed.

    You may perhaps recognize this approach; it is often called pessimistic locking.

  • Although not mandatory within the JDO specification, most JDO implementations suitable for use within a J2EE environment will also support optimistic locking.

    Here, it is possible for a persistent-capable object to reside in two different users' caches at the same time. As long as each only reads the data from the object, there are no issues (hence, “optimistic”).

    If one user modifies the data and commits their optimistic transaction, the new state of the object will be transparently written back to the data store. The other user does not necessarily know about this (though he or she can request to refresh the object instance if needed).

    Of course, issues do arise when both users modify the data. In this case, the first user to commit his or her transaction will succeed. When the second user attempts to commit his or her transaction, the PersistenceManager will throw a javax.jdo.JDOUserException, detailing that the state of the object has been changed by some other user since it was first instantiated in the cache.

From the data store's perspective, the optimistic transaction approach actually involves two data store transactions. The first is short-lived, lasting long enough just to read the data from the data store. The second will happen some time later (perhaps seconds, minutes, or longer) and again will be short-lived. During this second data store transaction, the persistent data will be updated with the modified data taken from the committing user's cache.

The JDO specification also defines the notion of JDO identity. There are three different ways for a JDO vendor to implement JDO identity, depending on the underlying technology:

  • For JDO implementations based on an O/R mapping tool, JDO identity basically corresponds to the primary key as defined in the RDBMS. The JDO specification terms this application identity or primary key identity.

  • For JDO implementations based on an OODBMS, JDO identity corresponds to the object ID as assigned by the OODBMS itself. The JDO specification calls this data store identity.

  • The final JDO identity type is perhaps likely to be least often used; it is simply called non-data store identity. This relates to objects unique within the JVM, but that are only ever written to (not read from) a data store (entries in a log file, for example).

The term application (primary key) identity is used for RDBMS-based identity because it is effectively the application itself that defines the semantics of the primary key. This has echoes in EJB where you, as the bean developer, are required to implement a primary key class or identify the primary key field. The RDBMS data store is expected to enforce the notion of identity through the use of unique indexes.

JDO identity is not the same as Java object identity, because in a single JVM, there could be many active PersistenceManagers; if optimistic transactions are used, multiple Java objects could be instantiated all with the same JDO identity. However, JDO identity does have some correspondence to Java's notion of equality (that is, where two objects are considered the same if equals() returns true). In particular, JDO requires that persistence-capable objects implement equals() in such a way that it returns true if and only if the JDO identity is the same. Unsurprisingly, this is exactly the requirement that EJB imposes on primary keys.

Note

JDO is very clear about identity, recognizing that more than one object instance may be instantiated for a single instance in the data store. In contrast, EJB is quite casual in this regard. Indeed, the EJB specification explicitly states that EJB containers have latitude to, for example, implement precisely one Entity bean per instance in the data store (option A, section 10.5.9) or to have multiple instances (option C).

Such latitude is surprising. Option A effectively means that Entity beans reside in a server-side cache. One consequence of this is that deadlocks must be handled in the EJB container. Meanwhile, option C effectively means that Entity beans reside in a client-side cache; any deadlocks arising are handled in the data store.


For any given persistence-capable object, only one of these different types of JDO identity applies. The JDO deployment descriptor (described briefly later) identifies the type of JDO identity in use.

javax.jdo Classes and Interfaces

The JDO API is defined by classes and interfaces in the javax.jdo package. Some of the interfaces are intended to be implemented by the JDO vendor and some by the application developer (but more on this in a moment). Figure 8.13 shows the main classes of the javax.jdo package.

Figure 8.13. The significant classes and interfaces of the javax.jdo package.


The javax.jdo.PersistenceManagerFactory, javax.jdo.PersistenceManager, javax.jdo.StateManager, and javax.jdo.Transaction interfaces are all implemented by the JDO vendor. The PersistenceManagerFactory has already been discussed. The PersistenceManager is the most important interface, because it provides the methods to control the lifecycle of the persistence-capable objects. Relating this back to EJB, you might think of it as combining the functions of the EJB container and of an EJB's home interface. The PersistenceManager also provides access to the current javax.jdo.Transaction. This allows the application developer to demarcate the transaction boundaries. In a J2EE environment, a CMTD EJB does not need to call the methods of this interface because the EJB container will do this work. If a BMTD Session bean is being developed to use JDO, the bean developer can use either the javax.jdo.Transaction or the javax.transaction.UserTransaction interface. The former is usually to be preferred however, because this allows a single PersistenceManager to be used across multiple transactions. If a UserTransaction is used, a PersistenceManager must be acquired for each and every transaction. Finally, the StateManager is used internally by the JDO implementation to keep track of changes to persistence-capable objects.

Of the two remaining interfaces shown in Figure 8.13, the javax.jdo.PersistenceCapable interface is implemented by the application developer. Hang on though! Isn't JDO meant to support transparent persistence? Doesn't that mean that there should be no requirement for the application classes to implement any sort of interface at all? Well, yes…and no. It is true that the persistence-capable classes developed by the application developer neither need to call the classes in javax.jdo, nor do they need to implement any of the interfaces in javax.jdo. However, before a persistent-capable class can be deployed into a JDO Implementation, it must be “enhanced.” An enhancer is a tool provided by the JDO vendor that manipulates the byte code of the compiled application classes. The resulting enhanced byte code represents a version of the application class that does implement the PersistenceCapable interface.

This whole process possibly sounds somewhat peculiar, but compare it to the approach used in EJB for CMP Entity beans. As you recall, the bean developer creates an abstract class with abstract getter and setter methods for each of the cmp-fields. The EJB container vendor's deployment tools then generate an implementation for those methods and create the database access code. The enhancement process effectively does the equivalent of the first of these two tasks and also imbues the application class with a reference to a StateManager instance. The StateManager itself takes on the job of database access and persistence.

The final interface, shown in Figure 8.13, is the InstanceCallbacks interface. The application developer can choose to implement this or not. If implemented, the methods give the application class visibility as to the transaction boundaries. You may have spotted that this is pretty similar to EJB's optional SessionSynchronization interface.

Time for some code! Listing 8.10 shows the basic steps to create a new persistence-capable Customer.

Listing 8.10. Using JDO to Create a New Customer
 1: // import javax.jdo.*;
 2: // import javax.naming.*;
 3:
 4: Context ctx = new InitialContext();
 5: PersistenceManagerFactory pmf = (PersistenceManagerFactory)
 6:     ctx.lookup("java:comp/env/jdo/SomePersistenceManagerFactory";
 7: PersistenceManager pm = pmf.getPersistenceManager();
 8:
 9: Transaction txn = pm.currentTransaction();
10: txn.begin();
11: Customer customer = new Customer(login, address1, address2, email, name);
12: pm.makePersistent(customer);
13: txn.commit();

That's honestly all there is to it! Customer is just a regular Java class that has been run through the JDO Enhancer.

Queries

As well as being able to create new objects, JDO also provides the facility to find existing objects. This is not surprising; after all, the home interface offers the finder methods as well as the create methods in EJB.

Rather than defining a declarative language, such as SQL or EJBQL, JDO uses a Java API approach. Figure 8.14 shows the classes and interfaces of javax.jdo that allow queries to be performed.

Figure 8.14. Classes and interfaces that support JDO queries.


The PersistenceManager.newQuery() method will instantiate a javax.jdo.Query object. The set of candidate objects to be returned by the query is then configured using the setCandidates() method. This is overridden to accept either a java.util.Collection or a javax.jdo.Extent (more on Extent shortly). A filter can also be defined using setFilter(), as can parameters to the filter. Variables are used to declare iterators over multi-valued collection fields.

The Extent interface is principally used to identify all instances of some specified class in the persistent data store. Conceptually, this could be a very large set of objects, so commercial JDO implementations are not expected to fully materialize the set. Indeed, the JDO specification explicitly requires that JDO implementations must not cause an out-of-memory error when instantiating an Extent. The java.util.Iterator returned by the iterator() method must be able to iterate over all instances if needed, but notice that other methods of the java.util.Collection (such as contains() and isEmpty()) are not present in the Extent interface. In practice, the Extent and Query implementations will be closely coupled to provide efficient ways to identify the qualifying persistence-capable objects.

As an example, Listing 8.11 shows two queries. The first identifies all jobs that have a customer of "winston"; the second finds all jobs that require "Cigar Trimmer" as a skill.

Listing 8.11. Using JDO to Search for Jobs that Meet Some Criteria
 1: // import javax.jdo.*;
 2: // import javax.naming.*;
 3:
 4: Context ctx = new InitialContext();
 5: PersistenceManagerFactory pmf = (PersistenceManagerFactory)
 6:     ctx.lookup("java:comp/env/jdo/SomePersistenceManagerFactory";
 7: PersistenceManager pm = pmf.getPersistenceManager();
 8:
 9: Transaction txn = pm.currentTransaction();
10: txn.begin();
11:
12: // query #1: all jobs that have a customer of "winston".
13: Query query1 = pm.newQuery();
14: query1.setClass(Job.class);
15: Extent candidateJobs = pm.getExtent(Job.class, false);
16: query1.setCandidates(candidateJobs);
17: query1.declareParameters("String nameParam");
18: query1.setFilter( "customer == nameParam ");
19: Collection query1Res = (Collection) query.execute("Winston");
20: for(Iterator iter = query1Res.iterator(); iter.hasNext(); ) {
21:     Job job = (Job)iter.next();
22:     System.out.println(job.getRef());
23: }
24:
25: // query #2: all jobs that require "Cigar Trimmer" as a skill
26: Query query2 = pm.newQuery(Job.class, candidateJobs);
27: query2.declareVariables("Skill eachSkill");
28: query2.setFilter( "skills.contains(eachSkill) && eachSkill.name == "Cigar Trimmer"");
29: Collection query2Res = (Collection) query.execute();
30: for(Iterator iter = query2Res.iterator(); iter.hasNext(); ) {
31:     Job job = (Job)iter.next();
32:     System.out.println(job.getRef());
33: }
34: txn.commit();

Other Features

There is much more in JDO than there is room to cover here. Some aspects worth a brief mention include the following:

  • Deployment descriptors— A deployment descriptor is used for each persistence-capable application class. This identifies the fields that are persistent rather than transient, the JDO identity type, and other information required for the JDO enhancer.

  • Second Class Objects (SCOs)— These are similar in concept to dependent value classes in EJBs, in that they are persistent only by virtue of being reachable from First Class Objects (all objects previous discussed have been First Class Objects). In particular, they do not have a JDO identity. If an SCO is modified, it must explicitly notify its containing First Class Object.

    The JDO specification identifies various classes in the JDK library packages that are first- or second-class, or that are not persistable at all (for example, java.net.Socket).

  • Lifecycle— Persistent-capable objects go through different stages of their lifecycle. For example, when an object is first instantiated, it is in the transient state. When the PersistenceManager.makePersistent() method is called, the object transitions to persistent-new state. One state, hollow, is very similar to the EJB notion of a passivated Entity bean.

  • Transient transactional objects— These are transactional objects acting in a manner akin to a ShoppingCart Session bean whose implementation supports automatic transaction recovery (implementing javax.ejb.SessionSynchronization).

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.118.12.157