Figure 1-1 is a
Unified Modeling Language (UML) diagram of the classes and
interrelationships in the Media Mania object model. A Movie
instance represents a particular movie.
Each actor who has played a role in at least one movie is represented by
an instance of Actor
. The Role
class represents the specific roles an
actor has played in a movie and thus represents a relationship between
Movie
and Actor
that includes an attribute (the name of
the role). Each movie has one or more roles. An actor may have played a
role in more than one movie or may have played multiple roles in a
single movie.
We will place these persistent classes and the application
programs used to manage their instances in the Java com.mediamania.prototype
package.
We will make the Movie
,
Actor
, and Role
classes persistent, so their instances
can be stored in a datastore. First we will examine the complete
source code for each of these classes. An import statement is included
for each class, so it is clear which package contains each class used
in the example.
Example 1-1 provides
the source code for the Movie
class. JDO is defined in the javax.jdo
package. Notice that the class
does not require you to import any JDO-specific classes. Java
references and collections defined in the java.util
package are used to represent the
relationships between our classes, which is the standard practice used
by most Java applications.
The fields of the Movie
class
use standard Java types such as String
, Date
, and int
. You can declare fields to be private;
it is not necessary to define a public get and set method for each
field. The Movie
class includes
some methods to get and set the private fields in the class, though
those methods are used by other parts of the application and are not
required by JDO. You can use encapsulation, providing only the methods
that support the abstraction being modeled. The class also has static
fields; these are not stored in the datastore.
The genres
field is a
String
that contains the genres of
the movie (action, romance, mystery, etc.). A Set
interface is used to reference a set of
Role
instances, representing the
movie’s cast. The addRole( )
method
adds elements to the cast
collection, and getCast( )
returns
an unmodifiable Set
containing the
elements of the cast
collection.
These methods are not a JDO requirement, but they are implemented as
convenience methods for the application. The parseReleaseDate( )
and formatReleaseDate( )
methods are used to
standardize the format of the movie’s release date. To keep the code
simple, a null
is returned if the
parseReleaseDate( )
parameter is in
the wrong format.
Example 1-1. Movie.java
package com.mediamania.prototype; import java.util.Set; import java.util.HashSet; import java.util.Collections; import java.util.Date; import java.util.Calendar; import java.text.SimpleDateFormat; import java.text.ParsePosition; public class Movie { private static SimpleDateFormat yearFmt = new SimpleDateFormat("yyyy"); public static final String[] MPAAratings = { "G", "PG", "PG-13", "R", "NC-17", "NR" }; private String title; private Date releaseDate; private int runningTime; private String rating; private String webSite; private String genres; private Set cast; // element type: Role private Movie( ) { } public Movie(String title, Date release, int duration, String rating, String genres) { this.title = title; releaseDate = release; runningTime = duration; this.rating = rating; this.genres = genres; cast = new HashSet( ); } public String getTitle( ) { return title; } public Date getReleaseDate( ) { return releaseDate; } public String getRating( ) { return rating; } public int getRunningTime( ) { return runningTime; } public void setWebSite(String site) { webSite = site; } public String getWebSite( ) { return webSite; } public String getGenres( ) { return genres; } public void addRole(Role role) { cast.add(role); } public Set getCast( ) { return Collections.unmodifiableSet(cast); } public static Date parseReleaseDate(String val) { Date date = null; try { date = yearFmt.parse(val); } catch (java.text.ParseException exc) { } return date; } public String formatReleaseDate( ) { return yearFmt.format(releaseDate); } }
JDO imposes one requirement to make a class persistent: a
no-arg constructor. If you do not define any
constructors in your class, the compiler generates a no-arg
constructor. However, this constructor is not generated if you define
any constructors with arguments; in this case, you need to provide a
no-arg constructor. You can declare it to be private
if you do not want your application
code to use it. Some JDO implementations can generate one for you, but
this is an implementation-specific, nonportable feature.
Example 1-2 provides
the source for the Actor
class. For
our purposes, all actors have a unique name that identifies them. It
can be a stage name that is distinct and different from the given
name. Therefore, we represent the actor’s name by a single String
. Each actor has played one or more
roles, and the roles
member models
the Actor
’s side of the
relationship between Actor
and
Role
. The comment on line [1] is used merely for documentation; it does
not serve any functional purpose in JDO. The addRole( )
and removeRole( )
methods in lines [2] and [3]
are provided so that the application can maintain the relationship
from an Actor
instance and its
associated Role
instances.
Example 1-2. Actor.java
package com.mediamania.prototype; import java.util.Set; import java.util.HashSet; import java.util.Collections; public class Actor { private String name; private Set roles; // element type: Role [1] private Actor( ) { } public Actor(String name) { this.name = name; roles = new HashSet( ); } public String getName( ) { return name; } public void addRole(Role role) { [2] roles.add(role); } public void removeRole(Role role) { [3] roles.remove(role); } public Set getRoles( ) { return Collections.unmodifiableSet(roles); } }
Finally, Example 1-3
provides the source for the Role
class. This class models the relationship between a Movie
and Actor
and includes the specific name of the
role played by the actor in the movie. The Role
constructor initializes the references
to Movie
and Actor
, and it also updates the other ends of
its relationship by calling addRole(
)
, which we defined in the Movie
and Actor
classes.
Example 1-3. Role.java
package com.mediamania.prototype; public class Role { private String name; private Actor actor; private Movie movie; private Role( ) { } public Role(String name, Actor actor, Movie movie) { this.name = name; this.actor = actor; this.movie = movie; actor.addRole(this); movie.addRole(this); } public String getName( ) { return name; } public Actor getActor( ) { return actor; } public Movie getMovie( ) { return movie; } }
We have now examined the complete source code for each class that will have instances in the datastore. These classes did not need to import and use any JDO-specific types. Furthermore, except for providing a no-arg constructor, no data or methods needed to be defined to make these classes persistent. The software used to access and modify fields and define and manage relationships among instances corresponds to the standard practice used in most Java applications.
It is necessary to identify which classes should be persistent and specify any persistence-related information that is not expressible in Java. JDO uses a metadata file in XML format to specify this information.
You can define metadata on a class or package basis, in one or
more XML files. The name of the metadata file for a single class is
the name of the class, followed by a .jdo suffix.
So, a metadata file for the Movie
class would be named Movie.jdo
and placed in the same directory as the Movie.class file. A metadata file for a
Java package is contained in a file named package.jdo. A metadata file for a Java
package can contain metadata for multiple classes and multiple
subpackages. Example 1-4
provides the metadata for the Media Mania object model. The metadata
is specified for the package and contained in a file named com/mediamania/prototype/package.jdo.
Example 1-4. JDO metadata in the file prototype/package.jdo
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE jdo PUBLIC [1] "-//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.prototype" > [2] <class name="Movie" > [3] <field name="cast" > [4] <collection [5] element-type="Role"/> </field> </class> <class name="Role" /> [6] <class name="Actor" > <field name="roles" > <collection element-type="Role"/> </field> </class> </package> </jdo>
The jdo_1_0.dtd file
specified on line [1] provides a
description of the XML elements that can be used in a JDO metadata
file. This document type definition (DTD) is standardized in JDO and
should be provided with a JDO implementation. It is also available for
download at http://java.sun.com/dtd. You can
also alter the DOCTYPE
to refer to
a local copy in your filesystem.
The metadata file can contain persistence information for one or
more packages that have persistent classes. Each package is defined
with a package
element, which
includes the name of the Java package. Line [2] provides a package
element for our com.mediamania.prototype
package. Within the
package
element are nested class
elements that identify a persistent
class of the package (e.g., line [3]
has the class
element for the
Movie
class). The file can contain
multiple package
elements listed
serially; they are not nested.
If information must be specified for a particular field of a
class, a field
element is nested
within the class
element, as shown
on line [4]. For example, you could
declare the element type for each collection in the model. This is not
required, but it can result in a more efficient mapping. The Movie
class has a collection named cast
, and the Actor
class has a collection named roles
; both contain Role
references. Line [5] specifies the element type for cast
. In many cases, a default value for an
attribute is assumed in the metadata that provides the most commonly
needed value.
All of the fields that can be persistent are made persistent by default. Static and final fields cannot be made persistent. A field declared in Java to be transient is not persistent by default, but such a field can be declared as persistent in the metadata file. Chapter 4 describes this capability.
Chapter 4, Chapter 10, Chapter 12, and Chapter 13 cover other
characteristics you can specify for classes and fields. For a simple
class like Role
, which does not
have any collections, you can just list the class in the metadata as
shown on line [6], if no other
metadata attributes are necessary.
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.