Enhancement


Enhancement adds code to the application data classes to integrate them with JDO. As a result of enhancement, the enhanced class implements the PersistenceCapable interface. When JDO acts on an application data object, it acts on it as a PersistenceCapable object. Because the PersistenceCapable interface is used only by the JDO implementation, it is found in the javax.jdo.spi package. Because the enhancer, instead of the application programmer, adds the implementation of the PersistenceCapable interface, the application programmer rarely, if ever, makes direct use of the PersistenceCapable interface.

The enhancer is a tool that the JDO vendor supplies. The enhancer adds methods and member fields to the application data class. Usually, enhancement occurs after class compilation, when the enhancer modifies the byte code of the compiled class. After enhancement, application data classes may be called enhanced classes to draw attention to their new form. The specification calls them PersistenceCapable classes.

Although it is typical for enhancement to be done after compilation and before deployment, vendors may choose to do it in some other way. JDO specifies the objectives of enhancement, but it does not specify how or when enhancement should occur. There are other approaches that vendors might use. They might enhance the class through the use of code generators. They might also enhance the class through the use of a specially designed class loader that enhances at runtime. It's sobering to think that they could simply leave it to the application programmer to cut and paste in the required code, but this error-prone method is not likely to be popular.

Although byte-code enhancement does not require the source code for the application data class, the application programmer needs to know many implementation details about the application data class. To enhance a class and use it effectively, the developer must know the names and types of the managed fields. Source code or documentation can provide this information. The developer can also obtain it by using the class disassembler that comes with the Java SDK. For example, to see all the fields of the FileInputStream class, enter the following command:

 javap -private java.io.FileInputStream 

The approach taken by the JDO specification to integrate application data classes with the persistence layer has some significant advantages. By using an interface, the application designer is free to use nearly any Java class as an application data class. By encouraging the provision of an enhancer tool, JDO moves the code required to make an application data class persistence capable into the vendor's tools. The vendor's enhancer produces the required code with no errors and little effort. Both the enhancer and the interface approach combine to keep the persistence service out of the design of the application data classes.

The Desired Effects of Enhancement

Numerous changes are made to the application data class during enhancement. Most changes occur for all application data classes, but some changes apply only to the topmost application data class in an inheritance tree. The types of changes include the following:

  • The class declaration is changed to indicate that it implements the PersistenceCapable interface.

  • The methods required by that interface are added.

  • A few member fields are added that the new methods use.

  • A pair of static getter-setter methods are introduced for each persistent field at the same access level as the field itself.

  • A static setter method is introduced for each transactional field at the same access level as the field itself.

  • All access to managed fields is converted to use the newly introduced static getter-setter methods.

  • A constructor that meets JDO's requirements is generated when not found.

  • Several changes are made to support standard object manipulations, such as cloning and serialization.

When the enhancer changes the class to implement the PersistenceCapable interface, the effect is the same as if the class had been written with the following declaration:

 class MyApplicationDataClass implements javax.jdo.spi.PersistenceCapable 

Enhancement adds roughly two dozen methods specified by the PersistenceCapable interface. The methods handle a variety of concerns for the JDO implementation, including JDO state interrogation, JDO identity, the construction of new objects, and the management of field values. When an application data object is persistent, it is associated with an implementation object called the state manager. The state manager connects a managed object with the JDO persistence service.

Enhancement adds a handful of member fields to the application data class. All of the fields are declared with either the static or transient modifier and, as a consequence, their values are not passed during serialization.

For easy identification and to prevent collisions with the names of the application-defined methods and data fields, all methods declared in the PersistenceCapable interface and all data member fields introduced into the application data class by enhancement start with the letters "jdo".

A pair of static getter-setter methods is created for each persistent field, and a static setter method is created for each transactional field. The static getter-setter methods have the same access level as the fields that they access. With few exceptions, all of the code within the application data class that accesses the member field is changed to use the corresponding static method. If the application data class implements the InstanceCallbacks interface, which Chapter 7 describes, the enhancer does not modify the application code within the jdoPreClear and jdoPostLoad methods.

The enhancer does not change the access level of a managed field. As a result, the enhancer allows unenhanced classes to compile against enhanced classes.

The Side Effects and Limitations of Enhancement

The purpose of enhancement is to make the application data class persistence capable. As a result of enhancement, side effects or potential side effects are introduced. In some cases, JDO takes precautions to avoid the potential side effects, and in other cases, the application programmer must work around the side effects if the particular effect is detrimental for the application. In a few cases, enhancement introduces limitations on the application data class that the programmer must observe. The next sections examine the potentially undesirable side effects of enhancement.

Enhancement and Constructors

If there are no constructors in the source code for a Java class, then the Java compiler generates a public no-arg constructor. If the source code defines at least one constructor, then the compiler does not generate the no-arg constructor. On the other hand, JDO requires a no-arg constructor in the application data class. The no-arg constructor can be any access level from private to public. If the enhancer does not find a no-arg constructor in the class, it adds a protected no-arg constructor.

JDO uses the no-arg constructor to instantiate the persistent object when its state is first retrieved from the datastore. The application programmer can, and almost certainly will, write other constructors that take arguments. These constructors may have any access level from private to public.

All constructors initialize unmanaged application data objects. During construction, the persistent fields have not been loaded from the datastore and the state manager is not available to load them. Before JDO can finish the initialization of the object and make it persistent, the no-arg constructor must return.

Consider JDO the Prime User of the No-Arg Constructor

JDO must use the no-arg constructor in the application data class, and in some ways, it is best to consider JDO the prime user of the no-arg constructor. In particular, the application may initialize the object differently in the no-arg constructor than it does in the other constructors. For example, if the application data class has a persistent HashSet field called dreams, initializing dreams to an instance of a HashSet in the no-arg constructor, as shown in the next line of code, can be counterproductive.

 dreams = new HashSet(); 

This assignment introduces three wasted steps every time JDO uses the constructor. Because the dreams field is persistent, the only value of the dreams field that the application sees when the object is persistent is the value created by JDO from persistent state. As a result, the construction of the new HashSet object, its assignment, and its garbage collection are all wasted steps. Without harm to the persistent object, the constructor could allow the value of dreams to remain null.

Although initializing persistent fields is counterproductive in the no-arg constructor, initializing either transactional or unmanaged fields is not counter-productive. JDO does not reinitialize transactional or unmanaged fields. Likewise, all other constructors can initialize all fields without waste, since JDO does not use these constructors. Often, the application uses the other constructors to create new objects that may be made persistent.

In some cases, the application must use the no-arg constructor. This happens when the application data class is a JavaBean. If the application uses the no-arg constructor and requires that persistent fields be initialized to nondefault values, then the application programmer must decide between taking the performance hit of wasted initialization, providing an initialization method that the application calls after construction, or implementing lazy initialization.

Reasons to Write the No-Arg Constructor in Every Application Data Class

In Java it is common to use field initializers for member fields that always take the same nondefault value regardless of which constructor is called. This idiom introduces a difficulty for byte-code enhancement when it must add a no-arg constructor. The compiler interprets the field initializers as meaning "put this code in all the constructors." Consequently, there is no byte code in the class file that corresponds directly to the field initializers. Instead, the compiler puts the byte code for the field initializers into each constructor. By the time byte-code enhancement occurs, the enhancer can only guess whether there were field initializers in the source code and what byte code corresponds to them. To avoid error, the enhancer is likely to make no guess at all. In this case, the field initializer code is missing from the enhancement-created no-arg constructor. To avoid this outcome, the application programmer should either avoid field initializers or write the no-arg constructor.

Every constructor of a derived class calls a super constructor of its parent class. If the call to the super constructor is not the first code in the constructor, the compiler inserts a call to the no-arg super constructor. When the no-arg constructor is not found, enhancement adds it to the application data class. The no-arg constructor that enhancement adds calls the no-arg constructor of the parent class. If the parent class is not an application data class (and therefore won't be enhanced) and if it does not have a no-arg constructor, then enhancement may end in an error condition or lead to a runtime error when the enhanced classes are used. By coding the no-arg constructor in the application data class, the programmer can detect the missing no-arg super constructor during compilation or direct the no-arg constructor to use a different constructor in the superclass.

As a result of the various small gotchas, it is good practice to always code the no-arg constructor for all application data classes. The sample no-arg constructor presented in Listing 5-1 satisfies JDO. The access modifier for the no-arg constructor may be anything from private to public. An access level of private is sufficient for JDO because the constructor is called from code that the enhancement step inserts into the class. If the application data class is a parent class for other derived application data classes, the access level cannot be private. If the application data class is used as a JavaBean, it must have a public no-arg constructor.

Listing 5-1: Sample No-Arg Constructor for Typical Application Data Class

start example
 private MyApplicationDataClass()    {    // used only by JDO    } 
end example

Tip

Always code a no-arg constructor in the application data class. It may be of any access level from private to public.

Enhancement and Serialization

The enhancement step provides complete support for serialization by providing additional modifications when the application data class implements Serializable. To support serialization, the enhancer modifies the least-derived application data classes that are assignment compatible with the Serializable interface.

When the enhancer adds code to the application data class to support serialization, it either generates a writeObject method, modifies an existing writeObject method, or modifies an existing writeReplace method. After the writeObject or writeReplace methods are enhanced, they call the jdoPreSerialize method in the PersistenceCapable interface. The jdoPreSerialize method ensures that all persistent fields are loaded with their persistent values prior to the object's serialization. When the enhancer generates the writeObject method, the method calls the jdoPreSerialize method and then invokes the defaultWriteObject method in the ObjectOutputStream.

Note

For more information on the role of the writeObject method in serialization, see the SDK's Javadoc for the java.io.ObjectOutputStream class. For more information on the role of the writeReplace method in serialization, see the Java Object Serialization Specification available on Sun's Web site.

Enhancement ensures that an object of an enhanced class can be serialized into the original unenhanced class, and vice versa. By inserting only static or transient member fields into the class, the enhancer ensures compatibility between the two versions of the class. If the enhancer does not find a serialVersionUID field in the unenhanced class, it inserts one into the enhanced class and uses the unenhanced class to calculate its value. These steps ensure that Java's serialization mechanism will find the post-enhancement class compatible with the pre-enhancement class.

When the serialization stream is deserialized, all of the deserialized objects are JDO-transient. JDO does not provide a way to deserialize a managed object. At its option, the application may deserialize into unenhanced classes.

Serialization starts with a root object. It serializes the primitive fields of the root object and follows its reference fields to other objects that are serialized in turn. Serialization skips fields marked with the static or transient keywords. Whether a field is serializable or transient for serialization is independent of whether it is persistent, transactional, or unmanaged for JDO. For that reason, any combination of the two sets of characteristics is possible. Serialization writes to the output stream all serializable fields, whether persistent, transactional, or unmanaged, and navigates to all serializable references until closure is reached. In short, enhancement does not interfere with or otherwise distort the expected result of serialization.

Because serialization invokes JDO's transparent persistence, serializing a persistent object may serialize many more objects than intended. As serialization follows the serializable references in persistent fields, JDO transparently retrieves the objects from the datastore. If the persistent data objects are sufficiently inter-connected by persistent and serializable references to each other, then the resulting graph of serialized objects may be as large as the number of objects in the datastore.

Serialize Application Data Objects or Data Transfer Objects?

The typical reason to serialize an application data object is to pass it by value to a remote client. Although application data objects can be serializable, some argue that data transfer objects, which are lightweight classes containing the state of interest, should be serialized instead. In this scenario, to satisfy a request, the application data service finds the persistent application data objects, creates the needed data transfer objects, and returns the data transfer objects to the client via serialization. Although this approach works, in some circumstances it is not required.

Data transfer objects solve two related problems that can sometimes be avoided by other means. Application data classes can define heavyweight objects with business logic that may not make sense in the remote client's context. Data transfer objects can avoid sending heavyweight objects to the client by transferring the state of interest into objects specifically designed to be lightweight. JDO supports another approach that can solve the same problem.

Although application data classes can define heavyweight objects, JDO also supports application data classes that define lightweight objects. If the application designer is able to define lightweight application data classes, then the problems of heavyweight persistent objects can be avoided entirely.

Data transfer objects also solve a second problem, the potential serialization of a large graph of objects. Consider again the small town library example used in Chapter 2. In the library example, each book refers to the categories to which it belongs, and each category refers to the books that it contains. Each borrowed book refers to the borrower who borrowed it, and each borrower refers to all the books that he has borrowed. As a result, returning one book by serialization may return all the books, all the borrowers, and all the categories. Data transfer objects flexibly and effectively prevent serialization from walking the entire datastore by allowing the designer to determine anew the associations between classes. JDO permits another less flexible, but equally effective, way to solve the same problem.

In JDO, serialization and persistence are independent properties. Consequently, the application designer can statically define, by using the transient keyword, where to cut the object graph that is returned by serialization. Listing 5-2 shows the modifications to the library classes, originally shown in Listing 2-3, that allow one book to be serialized with its categories. It also allows one borrower to be serialized with his borrowed books and their categories. In neither case does serialization return all the objects in the datastore.

Listing 5-2: Library Classes with Judicious Use of the transient Keyword

start example
 public class Borrower implements Serializable    {    // each of the fields below is a persistent field    private String name;    private HashSet books; // borrowed books    private Volunteer volunteer;    ...    } public class Volunteer implements Serializable    {    // each of the fields below is a persistent field    private int hoursPerWeek;    private Borrower borrower;    ...    } public class Book implements Serializable    {    // each of the fields below is a persistent field    private Date checkOutDate;    private String title;    private transient Borrower borrower;    private HashSet categories; // book's categories    ...    } public class Category implements Serializable    {    // each of the fields below is a persistent field    private String name;    private transient HashSet books; // books in this category    ...    } 
end example

In Listing 5-2, the transient keyword has been added to the borrower field in the Book class and to the books field in the Category class. Although these fields are now transient for Java's serialization, they remain persistent for JDO. Because these fields are no longer serialized, a response to a remote client can return a book, knowing that only the book and its categories are serialized. A response can also return a borrower, knowing that only the associated volunteer object, if it exists, and the list of books borrowed and their categories are returned. Because serialization skips the borrower field when it is serializing a Book object and skips the books field when it is serializing a Category object, the resulting graph of serialized objects is smaller than it would otherwise be.

As a result of the changes to the library classes, the meaning of a null value in the book's borrower field becomes more complex. (The situation for the books field in Category is similar.) On the server, if the borrower field is null, then the book is not borrowed. On the remote client, the book's borrower field is always null, because it is transient. As a result, the meaning of a null value changes with the object's genesis. Either the application data class or the code that uses the class must handle this additional complexity.

For many applications, statically cutting the object graph by judicious use of the transient keyword is a simple way to prevent serialization from walking the datastore. If more flexibility is needed, data transfer objects should be considered. The primary drawback of data transfer objects is that they create more objects on the server that must be garbage collected, and more classes that must be coded. It is possible that a future version of JDO will specify more control over the serialization of application data objects.

Tip

The JDO persistence layer completely supports serialization of enhanced classes, and for that reason, care must be taken that only the desired persistent objects are serialized.

Enhancement and Reflection

JDO makes no special accommodations for reflection. In particular, JDO does not provide transparent persistence during operations performed by reflection on member fields. As a result, when reflection is used to read or write a managed field's value, JDO is not called to perform its management tasks. Reflection can be used to call methods or constructors without harm, but using reflection to read or write managed fields should be avoided.

Tip

The data field operations of reflection are not suitable for enhanced classes because transparent persistence is not supported during the field get or set operations.

Enhancement and Introspection

Introspection discovers the properties, events, and methods of JavaBeans. It uses a class that implements the BeanInfo interface if found. If no BeanInfo class corresponding to the JavaBean is found, introspection uses reflection and naming conventions to discover the bean interface. Because enhancement adds dozens of methods to the application data class, if users are going to view the interface of the enhanced class through an introspection tool, the application programmer should provide a BeanInfo class to go with the application data class. The presence of the BeanInfo class at introspection time hides from the user the additional methods that enhancement adds.

Tip

If the application data class is a JavaBean that is introspected after enhancement, supply a corresponding BeanInfo class to hide the methods added by enhancement.

Enhancement and Deployment on Remote Clients

Because the enhanced class implements the PersistenceCapable interface and uses code in a static initializer to register the class with JDO, enhanced classes carry with them links to the classes and interfaces of the JDO specification. These links must be resolved when application data classes are loaded. Remote clients, which never use the JDO persistence service and deal only with unmanaged application data objects, still need to satisfy the links that enhancement adds. The JDO classes and interfaces are contained in the JDO Jar file that should be deployed with the enhanced application data classes. Because vendors may package the JDO classes as they see fit, the name of the Jar file containing the JDO classes may vary from implementation to implementation.

There are other deployment solutions that work for remote clients. Because remote clients do not use JDO and deal only with unmanaged application data objects, unenhanced application data classes can be deployed on the remote client. Enhancement enables serialization between the pre-enhanced and the post-enhanced application data classes. When unenhanced application data classes are deployed on the remote client, it may be possible to avoid deploying the JDO Jar file on the remote client. The JDO Jar file will still be required if the unenhanced application data class implements JDO's InstanceCallbacks interface or uses the JDOHelper class or the various JDO exception types.

Although it is simpler to deploy enhanced application data classes, along with the JDO Jar file, on the remote client, there are a couple of scenarios where it may make sense to deploy unenhanced classes. When the number of application data classes conspires with a limited memory footprint on the remote client, the code space saved by deploying unenhanced classes may be critical. Likewise, if the application data classes are particularly lightweight and the number of application data objects exceptionally large, the handful of extra fields added by enhancement may become a critical use of memory that can be eliminated by deploying unenhanced classes on the remote client. Another way to respond to these scenarios is to serialize data transfer objects rather than application data objects.

Tip

If the client is remote from the service that uses JDO, then either enhanced classes or unenhanced classes may be deployed on the client. If enhanced classes are deployed on the client, then the JDO Jar file must be deployed with them. If unenhanced classes that implement the InstanceCallbacks interface or use the JDOHelper class, which Chapter 7 covers, are deployed on the client, it is still necessary to deploy the JDO Jar file. Deploying unenhanced classes, instead of enhanced classes, saves quite a lot of code space per class and a small amount of data space per object.

Enhancement and Cloning

Java classes support cloning by implementing the java.lang.Cloneable marker interface. In the typical case, the class supports cloning by overriding the clone method of the Object class.

When clones are made, the fields inserted by JDO enhancement are copied along with all other field values. The enhancement step alters the clone method of the application data class to make the cloned object JDO-transient before the clone method returns. This change is required only in the least-derived application data class of an inheritance tree because the typical implementation of the clone method calls the clone method in its superclass.

In some cases, the application data class may make a clone without calling a clone method in an enhanced application data class. For example, the application data class may have a method like the following makeClone method:

 public class AppDataClass implements Cloneable    {    public AppDataClass makeClone() throws CloneNotSupportedException       {       return (AppDataClass) super.clone();       }    } 

In this example, AppDataClass inherits implicitly from Object. The enhancer does not alter the Object class when it enhances the AppDataClass. As a result, the call to the super.clone method does not invoke any code that is aware of JDO. To handle cases like this, JDO requires that the JDO implementation detect clones and make them JDO-transient on first contact.

Cloning does not invoke transparent persistence, and as a result, some of the cloned object's persistent fields can have Java default values instead of their persistent values. Usually, this is not the desired behavior. To ensure that persistent values are copied to the persistent fields of the cloned object, the application should override the clone method in Cloneable application data classes.

Listing 5-3 shows a clone method that makes a shallow copy. In this code, the clone method starts by calling the clone method in its superclass. This gets the memory state of all fields, but it does not invoke transparent persistence. To ensure that the persistent fields in the clone have the current persistent values, transparent persistence must be invoked. When myPersistentFieldOne is used on the right side of the assignment, transparent persistence fetches the current persistent value if it is not already in memory. (Alternatively, the application could ensure that the persistence manager's retrieve operation has been called on the object prior to cloning.)

Listing 5-3: A Shallow clone Method for an Application Data Class

start example
 public Object clone()    {    // call super to get a cloned object    MyApplicationDataClass obj = (MyApplicationDataClass) super.clone();    // JDO will insert code here during enhancement    // invoke transparent persistence to get    // the current values of persistent fields    obj.myPersistentFieldOne = myPersistentFieldOne;    obj.myPersistentFieldTwo = myPersistentFieldTwo;    ...    return obj;    } 
end example

Tip

Clones of persistent objects are always JDO-transient objects. JDO makes no provision to load persistent fields from the datastore prior to cloning the object. Loading the persistent values of persistent fields when creating a clone is a responsibility of the application.

Handling References in the Cloning of Persistent Objects

If the cloned object contains references to persistent objects, then errors arise if the application accesses these persistent objects after the persistence manager closes. Likewise, JDO throws a JDOUserException if persistent objects are accessed after the transaction ends when the NontransactionalRead property is false.

One way to prevent the clone from holding references to persistent objects is to clone or copy all the objects that persistent fields refer to. For example, if myPersistentFieldTwo in Listing 5-3 is a reference to an application data object whose class implements Cloneable, then instead of assigning the value as shown in Listing 5-3, a clone can be assigned instead. The following lines of code assign a clone to the cloned object's myPersistentFieldTwo:

 if (myPersistentFieldTwo != null)    obj.myPersistentFieldTwo = myPersistentFieldTwo.clone(); 

A clone method that invokes further cloning or copying of the object graph is called a deep copy. As with serialization, the danger of deep copy cloning is the possibility of walking, by virtue of transparent persistence, an object graph that includes a great many objects.

A second way to prevent the clone from holding references to persistent objects is to set the references to null, but this approach assumes that the class code permits the cloned object to function when the references are null.

Enhancement and Friendly Classes

Friendly classes are classes that directly access the data member fields of other classes. The access level of the member field determines which classes can be friendly. If the access level is public, then every class has direct access. If protected, then classes within the package or derived from the class have access. If the access level is not specified, it defaults to package-level access, which means that any class within the package has access. Finally, if the private access level is set, then no other classes (except inner classes) have access. In the sense that the term is being used here, a Java class is friendly if it has access to the instance fields of another class and if it uses this access to read or write the fields.

To provide transparent persistence, JDO converts the application code that directly uses the managed fields into calls to the static getter-setter methods introduced by enhancement. During enhancement, the enhancer makes these changes in all the application data classes that the JDO metadata identifies. The enhancer must also change friendly classes that are not themselves application data classes but that directly access the managed fields of application data classes. The specification calls these enhancer-modified friendly classes persistence aware.

The JDO metadata identifies for the enhancer the application data classes that it must enhance to support the PersistenceCapable interface, but the JDO metadata does not identify the persistence-aware classes. Each vendor's enhancer specifies the required steps that ensure the enhancement of the persistent-aware classes. For this reason, specifying the persistence-aware classes is an implementation-dependent step in the build or deployment process.

By now, if you have been coding in Java for any period of time, you have been hit on all sides by the mantra of encapsulating the implementation and choosing carefully the interface to expose. JDO's enhancement of application data classes provides one more reason to encapsulate the member fields of application data classes. If managed fields are restricted to private fields, then it is fairly easy to ensure that all classes that access the field are enhanced. If friendly access is allowed, then it becomes likely that some friendly class will be deployed that should have been enhanced, but was not. The behavior that results from friendly classes making direct, unenhanced access to persistent fields can be difficult to debug.

Tip

To avoid late night debugging, always declare the managed fields with the private modifier. When wider access to these fields is needed, write getter-setter methods.

Enhancement and Inheritance

Application data classes may inherit from any class. They typically inherit from Object, or from an application class that is not enhanced, or from another application data class that is enhanced. In the normal case, any class that inherits from an enhanced application data class is itself an enhanced application data class, but this is not a JDO requirement. In JDO, unenhanced classes can inherit from enhanced classes, and enhanced classes can inherit from unenhanced classes, without limitation.

The most-derived application data classes must be concrete classes. The other application data classes in the inheritance tree may be concrete or abstract.

JDO can manage and persist only the objects of enhanced classes and of supported system classes. JDO cannot manage or persist the objects of an unenhanced class, even if the class is the superclass of an enhanced class or the derived class of an enhanced class.

An example of an inheritance hierarchy that mixes enhanced and unenhanced classes is shown in Figure 5-1. There are three application defined classes, MyClassAlpha, MyClassBravo, and MyClassCharlie, arranged in an inheritance hierarchy. The java.lang.Object class is the base class of the hierarchy. MyClassAlpha and MyClassCharlie are enhanced application data classes. MyClassBravo is not an enhanced class. The application can create instances of MyClassAlpha and MyClassCharlie and make them persistent. The application can create instances of MyClassBravo, but it cannot make them persistent. Instances of MyClassAlpha and MyClassCharlie can be retrieved from the datastore, but instances of MyClassBravo cannot. For some applications, the ability to have an unenhanced class between two enhanced classes in the inheritance tree may prove useful, but for most applications, any class that inherits from an enhanced class is itself an enhanced class.


Figure 5-1: An inheritance tree that mixes enhanced and unenhanced classes

The type of JDO identity (either application, datastore, or nondurable) of the least-derived enhanced class determines the identity type for all of its descendents in the inheritance tree. Taking the example in Figure 5-1, if MyClassAlpha uses datastore identity, then MyClassCharlie must also use datastore identity. When application identity is used, one application identity class may be used for the entire tree of enhanced classes. In this case, the key fields are defined in the least-derived enhanced class, which may be an abstract class.

Tip

JDO provides excellent support for inheritance in the data object model. It is best to define key fields in the least-derived enhanced class. All application data classes that share a common enhanced superclass must use the same type of JDO identity.

Enhancement and Inner Classes

In Java, inner classes come in four flavors: static, member, local, and anonymous local. All inner classes may be friendly to their outer classes, and vice versa. Objects of inner classes may access all the fields and methods of their outer class, including the private fields and methods. Likewise, the objects of outer classes have full access to the fields and members of their inner classes.

Instances of member, local, and anonymous local inner classes receive during construction an implicit reference to the containing object of their outer class. Static inner classes, on the other hand, do not receive an implicit reference to an object of their outer class. For this reason, when static inner classes need to refer to an object of their outer class, they must be passed a reference.

JDO can enhance static inner classes, but it cannot enhance member, local, or anonymous local inner classes. Because all inner classes are friendly to their outer class, object modeling needs that are normally met by using member, local, or anonymous local classes can be met by using static inner classes. In most cases where member, local, or anonymous local classes have been used, the code also uses the implicit reference to the containing object of the outer class. To provide the same functionality when using a static inner class, the code must pass an explicit reference to the object of the outer class.

Tip

JDO can enhance static inner classes, but it cannot enhance the other types of inner classes. Because all inner classes are friendly to the outer classes that contain them, the availability of static inner classes is sufficient for modeling purposes.

Enhancement and Performance

Enhancing a class adds a fair amount of code to the class. The file size of a minimal class such as those used in the examples in this book could be only 2 kilobytes, but enhancement grows it to about 8 kilobytes. Nearly all of this increase is code. Enhancement adds less than a handful of instance fields to the application data class. As a result, the memory consumption of loading a thousand objects of an application data class is only mildly increased by enhancement.

When a field's value is fetched in an unenhanced class, the fetch occurs directly from the field's memory location, but when a persistent field's value is fetched in an enhanced class, the operation, due to transparent persistence, is more complex and time consuming. If the field's value has not been loaded from the datastore, the fetch causes JDO to read the value from the datastore. The read slows down the fetch considerably. Under typical conditions, a read from the datastore takes several orders of magnitude longer than a fetch from memory. Nearly all of this time is spent in the network transport and database server. JDO has very little to do with it. Any code that uses the database feels the same impact.

On second and subsequent fetches of the persistent field's value, performance is still slowed by the presence of the enhancement-added code. The additional code can easily cause second and subsequent fetches to take 40 percent longer than fetches from the same field in unenhanced code, even though the value is now stored in the field. The price of enhancement continues to be paid even when the enhanced objects are themselves unmanaged. JDO is entirely responsible for this negative impact on performance.

Although a 40 percent increase in time to fetch the value of a memory field sounds very significant, in fact, it is more likely meaningless for the typical application. The typical application spends a very tiny fraction of its overall time fetching the values of loaded persistent fields. As a result, increasing this tiny fraction of time by 40 percent is usually insignificant. For most applications, this performance hit is a modest price to pay for transparent persistence.

Tip

Enhancement is usually not critical for memory consumption or for performance, but all applications, whether they use JDO or not, play a high price for accessing a datastore. Consequently, application developers must continue to carefully design what to persist and when to persist it.

Enhancement and Debugging

JDO does not disturb the source code line numbers when it enhances an application data class. As a result, the programmer can debug the enhanced class with the original source code. JDO implementations have enjoyed an excellent track record, even during beta, in meeting this requirement.

During debugging, the code added by enhancement is present but not visible in the debugger. There are two ways to see the enhanced code in the debugger. Some vendors provide a means to generate the enhanced source code. Failing that, the enhanced application data class can be decompiled. The JODE decompiler from http://sourceforge.net/projects/jode does a good job. After compiling the source code that contains the enhancement-added code, you can debug the class with the enhancement-added code in full view.

Viewing the enhancement-added code in the debugger is rarely required. The code that the enhancer adds is probably the most thoroughly tested code of the implementation. Most JDO bugs will be in the implementation's runtime rather than in the code that the enhancer adds to the application data classes.

Tip

Enhancement preserves the line numbers in the class file to allow continued visual debugging of the enhanced application data class using the original (unenhanced) source code.

Enhancement and the Fear of Code Mangling

In programming, as in other fields of endeavor, perception can be as important as reality. For some developers, the idea that their wonderfully coded classes will be enhanced leads to the fear that their code will be mangled. But, in fact, there really is no difference between calling a library method to perform some task and having the code of that method embedded by tools into the application's class. Every programmer's code lives or dies in a web of other people's code over which he has only limited control. Once this reality is recognized, the skills to deal with it can be developed.

Although enhancement makes many alterations to the application data class, the negative side effects are few and marginal. Most negative side effects of enhancement can either be tolerated or worked around. Enhancement allows the application programmer to enjoy the benefits of transparent persistence with little work and few surprises.




Using and Understanding Java Data Objects
Using and Understanding Java Data Objects
ISBN: 1590590430
EAN: 2147483647
Year: 2005
Pages: 156
Authors: David Ezzio

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net