4.7. Code Notes and J2EE Tips

  1. JDO persistence capable instances are excellent candidates for the Data Transfer Object Pattern. This pattern attempts to reduce network load, as well as group business logic into concise units. For example, CarBean.edit allows you to ensure that all values are correct before committing a transaction, instead of sequentially calling getters and setters on the session facade. This is especially true when using RMI such as from a Swing based application connecting to an application server.

    CarEJB works as a session bean facade to demarcate transactions, provide finder methods, and encapsulate complex business logic at the server level.

  2. Persistent instances using datastore identity lose all identity upon serialization (which happens when they are returned from an EJB method). While there are numerous ways to solve this problem, the sample uses the clientId field of Car to store the stringified datastore id. Note that this field is not persistent. This field ensures that the client and EJB always share the same identity for a given car instance.


    Note that this situation is slightly different for application identity. While persistent instances under application-identity still lose their id instance, recreating it from the server side is trivial as the fields on which it is based are still available.

    Other ways of solving this problem include:

    • Transmitting the object id first or otherwise storing identity with the client:

      Object oid = dogRemote.findByQuery ("// some query");
      Dog dog = dogRemote.getDogForOid (oid);
      // changes happen to dog
      dogRemote.save (oid, dog);
    • Wrapper objects that store the persistent instance, object id instance, as well as potentially other application-specific data:

      public class DogTransferObject
          private Dog dog;
          private Object oid;
          private String authentication; // some application specific client data
  3. PersistenceManager.close should be called at the end of every EJB method. In addition to ensuring that your code will not attempt to access a closed persistence manager, it allows the JDO implementation to free up unused resources. Since a persistence manager is created for every transaction, this can increase the scalability of your application.

  4. You should not use PersistenceManager.currentTransaction or any javax.jdo.Transaction methods. Instead, use the JTA transactional API. SampleUtilities includes a helper method to obtain a UserTransaction instance from JNDI (see your application server's documentation on the proper JNDI lookup).

  5. While serialization of PC instances is relatively straightforward, there are several things to keep in mind:

    • Collections and iterators returned from query instances become closed and inaccessible upon method close. If the client is receiving the results of a query, such as in CarBean.query , the results should be transferred to another fresh collection instance before being returned from the server.

    • While "default fetch group" values will always be returned to the client upon serialization, lazily loaded fields will not as the persistence manager will have been closed before those fields attempt to serialize. One can either simply access those fields before serialization, or one can use the persistence mangaer's retrieve and retrieveAll methods to make sure object state is loaded. Note that these methods are not recursive. If you need to go through multiple relations, you must call retrieve at each relational depth. This is an intentional limitation in the specification to prevent the entire object graph from being serialized and/or retrieved.

  6. It is not necessarily required that you use EJBs and container-managed transactions to demarcate transactions, although that is probably the most common method. In EJBs using bean managed transactions, you can control transactions through either the javax.jdo.Transaction or the javax.transaction.UserTransaction. Furthermore, outside of EJBs you can access the JDO layer with either transactional API.

  7. The Kodo distribution includes source code for some convenient base classes that encapsulate much of the code that is laid out in this example for clarity. JDOBean , JDOSessionBean, and JDOEntityBean include most of the funcationality of SampleUtilities, and handle common EJB interface methods such as setEntityContext. To use these classes, we recommend placing Kodo's jars into the system classpath and not into the ear. Ear deployment can cause classloader problems due to the multiple locations that these classes could be loaded from.

  8. Persistence managers are allocation on a per-Transaction basis. Calling getPersistenceManager from the same persistence manager factory within the same EJB method call will always return the same instance.

    SamplesUtilities.getPersistenceManager () 
        == SampleUtilities.getPersistenceManager (); // will always be true