You can make most of your classes persistent in a JDO environment.
JDO has the ability to make plain ordinary Java
objects (POJOs) persistent. This includes classes that
represent the entities in your application domain, utility classes that
model other data, and abstractions you need to support your
application’s functionality. Your classes can also use all of Java’s
class and field modifiers, including: private
, public
, protected
, static
, transient
, abstract
, final
, synchronized
, and volatile
. In some cases, as we will explore later in this chapter,
some of these modifiers cannot be used with persistent fields.
The persistent state of a persistent class is represented entirely
by the values of its Java fields. If you have a class that has some
state that needs to be preserved and it depends on inaccessible or
remote objects (e.g., it extends java.net.SocketImpl
or uses Java Native Interface (JNI)), you cannot make the class
persistent. You also cannot have a persistent nonstatic inner class, because the state of the inner
class instance depends on the state of its enclosing instance.
With a few exceptions, system-defined classes (those defined in
java.lang
, java.io
, java.net
, etc.) cannot be persistent. They are
also not allowed to be the type of a persistent field. This includes
classes such as System
, Thread
, Socket
, and File
. We list the system classes that are supported in Table 4-2 later in this
chapter. You may be using an implementation that supports additional
system-defined classes, especially those for modeling state information.
Relying on support for these additional types will make your software
dependent on that implementation.
As discussed in Chapter 1, each persistent class needs to have a no-arg constructor. If your class does not define any constructors, the Java compiler generates a no-arg constructor automatically (called the default constructor). But if you do define one or more constructors with arguments in a persistent class, then you must also define a no-arg constructor manually.
When your application first accesses a persistent instance, the JDO implementation needs to construct an instance, so it calls the no-arg constructor. The availability of a no-arg constructor is the only requirement JDO imposes on your persistent classes. Some JDO enhancers can generate this no-arg constructor for you if it does not already exist, but they are not required to do so.
You may not want other classes in your application calling the
no-arg constructor. If this is the case, you can declare it to be
private
. Or, if the class will have
subclasses, declare it to be protected
so that the subclass constructors
can call it.
Every class that you want to be persistent must be declared in a JDO metadata file. This file cannot include any system classes. Any class that is not declared in a metadata file is a transient class, except for the system classes that all implementations support. You typically place additional persistence-related information that is not expressable in Java in the metadata file. This metadata is used when a class is enhanced and also at runtime.
JDO metadata is stored in XML format. An XML Document Type Definition (DTD) defines the elements in a JDO metadata file. The JDO DTD is provided in Appendix B. It should be identical across all implementations.
You can place the metadata for your application’s classes in one or more XML files. A few rules exist for the naming and directory placement of metadata files to assure portability among implementations. For portability, metadata files should be available via resources loaded by the same class loader as the persistent classes.
If you have a metadata file that contains information for a package or multiple packages, then the name of the XML file should be package.jdo. (Here we literally mean the word “package,” not the name of an actual Java package.) The package.jdo file can be placed in one of the following directories:
- META-INF
In this case, package.jdo can contain metadata for any class in your application.
- WEB-INF
Files like package.jdo should be placed in this directory when deploying a JDO application in a web container.
- (no directory)
The package.jdo file is not in any subdirectory of the classpath.
- <package>
The package.jdo file is placed in the subdirectory that corresponds to the package defined in the metadata. Thus, if package.jdo contains the metadata for the
com.mediamania.content
package, it would placed in the com/mediamania/content directory.
If you have a metadata file that only contains information for a single class named classname, then its filename should be classname.jdo and it should reside in the same directory as the class file, based on the package of the class.
When the JDO implementation needs metadata for a class and the metadata has not been loaded yet, the metadata is searched in the following order:
META-INF/package.jdo
WEB-INF/package.jdo
package.jdo
<package>/package.jdo
<package>/<class>.jdo
where <package> represents the directory corresponding to the package of the class and <class> represents the name of the class.
A search for the metadata for the Customer
class in the com.mediamania.store
package is performed
in the following order:
META-INF/package.jdo
WEB-INF/package.jdo
package.jdo
com/package.jdo
com/mediamania/package.jdo
com/mediamania/store/package.jdo
com/mediamania/store/Customer.jdo
If no metadata is found for the Customer
class in any of these locations,
it is considered a transient class.
Once the metadata for a class has been loaded, it is not replaced. Metadata contained in a file higher in the search order is used instead of metadata lower in the search order. This search order is optimized so that implementations can cache metadata as soon as it is encountered, reducing the number of file accesses that are needed to load the metadata.
Metadata that is not in its natural location may override
metadata that is in its natural location. For example, when the JDO
implementation searches for the metadata for com.mediamania.content.Movie
, it may find
the metadata for the com.mediamania.store.Rental
class in the
com/mediamania/package.jdo
file. In this case, a subsequent search for the metadata for
com.mediamania.store.Rental
will
use the metadata that has already been cached, instead of looking in
com/mediamania/store/package.jdo or
com/mediamania/store/Rental.jdo.
These rules for the name and location of the metadata files apply both during enhancement and at runtime. From now on, the term “metadata” refers to the aggregate of all the JDO metadata for all packages and classes, regardless of their physical packaging in multiple files and directory placement.
The jdo
element is the
highest-level XML element in the metadata hierarchy. It does not
have any attributes of its own. It contains one or more nested
package
elements. A package
element is
used to represent a specific Java package. It has a single required
attribute, called name
, that
contains the completely qualified name of the Java package.
Within a package
element,
you can nest one or more class
elements. A
class
element identifies a
specific Java class in the enclosing package as persistent. The
class
element’s only required
attribute is name
, which is
given the name of the class. You should only list classes in the
metadata that you want to be persistent.
The class
element has the
following additional optional attributes:
The identity-type
attribute indicates which type of identity should be used with the
class. It defaults to datastore identity, which does not require any
additional effort from you. The objectid-class
attribute identifies a class defined by the application to serve as
the application identity of the class. Chapter 10 covers the various
forms of identity in detail; until then, we will use datastore
identity in all of our examples. The requires-extent
attribute indicates
whether an extent is maintained for the class. Extents are covered
in Chapter 8. The persistence-capable-superclass
attribute
identifies the closest superclass in the inheritance hierarchy that
is persistent, if there is one.
The extension
element
specifies vendor-specific metadata extensions in a uniform manner.
All JDO metadata elements can have nested extension
elements. The required
vendor-name
attribute associates the extension with a specific vendor. Each
vendor uses a unique name to identify metadata extensions for their
implementation. The vendor name "JDORI
" is reserved for use with the JDO
reference implementation. A JDO implementation ignores any extension
elements that have a vendor-name
value that does not correspond
to their implementation. The extension
element also has optional
key
and value
attributes.
A key
may or may not have an
associated value
. The vendor
chooses values for these attributes that they recognize and
interpret. Consult your documentation to see what metadata
extensions are provided.
The following illustrates the hierarchical nesting of metadata elements:
jdo package class field collection extension extension field map extension field array extension extension extension extension extension
One or more extension
elements can be nested within each of these elements (including
extension
itself) to provide
vendor-specific information. The field metadata elements (field
, collection
, map
, and array
) are covered later in this
chapter.
Each class in an inheritance hierarchy can be transient or persistent, independent of the persistence of other classes in the hierarchy. Thus, a class can be persistent, even if its superclass is not. This allows you have a persistent class that extends a transient class that was not designed to be persistent. Likewise, a subclass of a persistent class may be transient or persistent.
If a persistent class has one or more persistent superclasses,
the class
element’s persistence-capable-superclass
attribute
must identify the most immediate persistent superclass. If the
superclass is in a different package, it must be specified with its
fully qualified name. If the superclass is in the same package, you
can omit the package qualifier. You may wonder why you need to specify
this in the metadata. After all, the Java class declarations specify
the branch of superclasses from a class up to Object
in an inheritance hierarchy, and your
metadata identifies which of these classes are persistent. But the
metadata for a superclass may be specified in a different metadata
file. JDO is designed such that the enhancer can enhance a class in a
stateless fashion, independent from other classes. The order in which
classes are enhanced is irrelevant, and a class can be enhanced
without the presence of any other classes. This greatly supports the
simplicity of enhancer design, ease of use, integration with
classloaders, and—last, but not least—easy reproducability of
errors.
To illustrate these concepts, the UML diagram in Figure 4-1 describes two inheritance hierarchies. We use the stereotyping facility in UML to indicate whether a class is persistent or transient. In practice, you are not likely to have an inheritance hierarchy with such a complicated mix of persistent and transient classes. In many cases, the classes in an inheritance hierarchy are either all transient or all persistent. But JDO provides you with the flexibility to choose whether each class in an inheritance hierarchy is transient or persistent, as we have demonstrated here.
The following metadata identifies the persistent superclass for each persistent class shown in Figure 4-1. This metadata is placed in the com/mediamania/inheritexample/package.jdo file.
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE jdo PUBLIC "-//Sun Microsystems, Inc.//DTD Java Data Objects Metadata 1.0//EN" "http://java.sun.com/dtd/jdo_1_0.dtd"> <jdo> <package name="com.mediamania.inheritexample" > <class name="A" /> <class name="C" persistence-capable-superclass="A"/> <class name="E" persistence-capable-superclass="A"/> <class name="G" persistence-capable-superclass="C"/> <class name="H" persistence-capable-superclass="A"/> <class name="K" /> <class name="M" /> <class name="O" persistence-capable-superclass="K"/> </package> </jdo>
Let’s examine the object model we use in most of the examples
throughout this book. Media Mania, Inc. provides a system in their
stores that contains information about the various forms of media that
customers can rent or purchase. In Chapter 1 we created a prototype
application contained in com.mediamania.prototype
. Now, we replace
this prototype with two new packages: com.mediamania.content
and com.mediamania.store
.
The com.mediamania.content
Java package contains classes that represent generic media content
information. The content handled by the stores includes movies and
games. The Movie
and Game
classes extend an abstract base class
called MediaContent
. The Studio
class contains information about the
studio that produced the game or movie. Figure 4-2 illustrates the
relationships among these classes.
Each person involved in a movie, as either the director or an
actor, is represented by an instance of MediaPerson
. Figure 4-3 illustrates the
relationships among Movie
and
MediaPerson
instances.
A Movie
instance has one or
more Role
instances representing
the cast of the movie. It also has a reference to the MediaPerson
for the director of the movie.
We assume a movie has a single director (though in real life this is
not always the case). The Role
class references its Movie
and a
MediaPerson
who served as the actor
for the particular role. Given a specific MediaPerson
instance, it is possible to
access all the movies they directed and all the roles they have played
in movies. This model also allows for an actor who has played multiple
roles in the same movie.
In addition to the media content information, each store tracks
the rental and purchase activities of its customers. The com.mediamania.store
package contains the
classes representing store-specific information. Figure 4-4 illustrates the
relationships among these classes.
Figure 4-4. Classes in the com.mediamania.store package (except MediaContent in the content package)
Each customer that has rented or purchased some media content at
the store is represented by an instance of the Customer
class. An Address
instance contains address
information for the customer. The store tracks two kinds of
transactions: rentals and purchases. These are represented by Rental
and Purchase
classes that extend a Transaction
base class. The store tracks the
current items the customer has out for rent and also keeps a history
of all the customer’s transactions.
A MediaItem
instance
represents a particular format of a given MediaContent
item. For example, a Movie
can exist in VHS and DVD formats and a
Game
may be supported in formats
for the Playstation, Playstation 2, Xbox, and Nintendo GameCube. The
stock of media items is designated as items to be sold or rented. A
RentalItem
instance exists for each
individual item that can be rented to a customer. The items in stock
that are currently available for rent are represented by RentalItem
instances that have a null
value for their currentRental
field. The model does not
track the individual items that are sold, but the MediaItem
class tracks how many items for
purchase are in stock and how many have been sold year-to-date. Each
Purchase
instance contains a
reference to the specific MediaItem
that the customer bought.
The store has different rental policies and prices, based on the
popularity of an item and how recently it became available. A RentalCode
instance maintains information
about a particular rental policy. Each MediaItem
instance is associated with a
particular RentalCode
, which may
change over time.
A Rental
instance represents
a customer’s rental of a particular media item; it references the
specific RentalItem
rented. This is
necessary so the store can track which item has been rented and update
the customer account when it is returned, taking into account any late
fees that may be due. The RentalCode
associated with the MediaItem
at the time of rental is
associated with the Rental
instance. This is necessary because the RentalCode
for a MediaItem
will change occasionally.
Appendix E provides all
the classes for the model. The following metadata specifies the
packages and persistent classes for the object model. Since it
contains metadata information for the com.mediamania.content
and com.mediamania.store
packages, we place the
metadata in a file named com/mediamania/package.jdo, based on their
common base package name.
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE jdo PUBLIC "-//Sun Microsystems, Inc.//DTD Java Data Objects Metadata 1.0//EN" "http://java.sun.com/dtd/jdo_1_0.dtd"> <jdo> <package name="com.mediamania.content" > <class name="Studio" > </class> <class name="MediaContent" /> <class name="Movie" persistence-capable-superclass="MediaContent"> </class> <class name="MediaPerson" > </class> <class name="Game" persistence-capable-superclass="MediaContent"> </class> <class name="Role" /> </package> <package name="com.mediamania.store" > <class name="MediaItem" > </class> <class name="RentalItem"/> <class name="Customer" > </class> <class name="Address" /> <class name="Transaction" /> <class name="Purchase" persistence-capable-superclass="Transaction"/> <class name="Rental" persistence-capable-superclass="Transaction"/> <class name="RentalCode" /> </package> </jdo>
The metadata lists each persistent class in the content
and store
packages. If an inheritance
relationship exists, the metadata specifies the persistent superclass.
Later in this chapter, we will add more information that provides
information about the fields and relationships.
Get Java Data Objects now with the O’Reilly learning platform.
O’Reilly members experience books, live events, courses curated by job role, and more from O’Reilly and nearly 200 top publishers.