Chapter 6. Code Notes and J2EE Tips

  1. JDO PersistenceCapable instances make for top candidates for using 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 one 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 to encapsulate complex business logic at the server level.

  2. PersistenceCapable instances using datastore identity lose all identity upon serialization (which happens when it is 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 String version of the 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]Note

    Note that this situation is slightly different for application-identity. While PersistenceCapable instances under application-identity still lose their id instance, recreating it from the server side should be 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 PersistenceCapable instance, object id instance, as well as potentially other applicatio0 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 PersistenceManager, it allows the JDO implementation to close up resources no longer needed. Since a PersistenceManager is created for every transaction, this can increase the scalability of your application.

  4. You should not use PersistenceManager.currentTransaction () or any other javax.jdo.Transaction methods. Instead, use the JTA transactional API instead. 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 straight forward, 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.

    • While "default-fetch-group" values will always be returned to the client upon serialization, lazily loaded fields will not as the PersistenceManager will have been closed before those fields attempt to serialize. One can either simply access those fields before serialization, or one can use the utility methods retrieve () and retrieveAll () methods in PersistenceManager. Note that these methods are not recursive. If one needs to go through multiple relations, retrieve () needs to be called 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 one uses EJBs and container-managed transactions to demarcate transactions, although that is probably the most common. By obtaining a javax.transaction.UserTransaction, one can finely control transactions in a bean-managed EJB. Furthermore, one could access the JDO layer using the UserTransaction API free of EJBs altogether, bypassing the JDO transaction API. While not preferred, this technique could prove useful in tailoring small portions of your application outside the EJB context for finer transactional control.

  7. We ship compiled and included in our source 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, as well as handling common EJB interface implementations such as setEntityContext (). To use these classes, it is recommend that you put Kodo's jars into the system classpath and not into the ear. This can cause classloader problems due to the multiple locations that these classes could be loaded from.

  8. PersistenceManagers are allocation on a per-Transaction basis. Calling getPersistenceManager () from the same PersistenceManagerFactory within the same EJB method call will always return the same instance.

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