Fields contain the state of an instance. JDO provides for the access, management, and storage of an instance's fields in a datastore. All of Java's field type categories are supported: primitive types, reference types, and interface types. JDO also supports all of Java's field modifiers, including private, public, protected, static, transient, final, and volatile. But static and final fields cannot be persistent, as we will discuss later in this chapter.
As we explained earlier, you can have both transient and persistent instances of a persistent class. The individual fields of a persistent class can also be transient or persistent for all of the class's persistent instances. A field's type and modifiers determine whether it is persistent or transient, by default. You can override the default persistence of a field in the metadata. We cover transient fields later in this chapter.
You can specify persistence-related information about a field by using the field metadata element. Its required name attribute should have the name of the field in the Java class declaration. It has attributes to control the field's persistence and the type of its elements if it is a collection. We cover these attributes later in this chapter. If the class uses application identity, one or more fields need to indicate they are a primary-key field; Chapter 10 covers this in detail. Chapter 12 addresses advanced field-management facilities enabled by the remaining field element attributes.
You do not need to provide metadata for every field in a class. Default values are assumed for any fields that lack metadata declarations. These default values usually provide the behavior that you need. So, in many circumstances, you do not need to provide field metadata.
You cannot make many system-defined classes persistent, nor can you have a field of a system-defined class. Table 4-2 lists the system-defined types in the Java language environment that JDO implementations do support.
Primitives |
java.lang |
java.util |
java.math |
---|---|---|---|
boolean |
Boolean |
Locale |
BigInteger |
byte |
Byte |
Date |
BigDecimal |
short |
Short |
HashSet | |
char |
Character |
Collection | |
int |
Integer |
Set | |
long |
Long | ||
float |
Float | ||
double |
Double | ||
String | |||
Number | |||
Object |
You can declare a field to refer to a persistent class instance. In addition, you can use Java's polymorphism to declare a field that refers to a base class and have it reference a subclass instance. You should be accustomed to using polymorphic references in your object models. Object databases have supported them for many years, but this modeling capability has not been available in relational database schemas and interfaces. The JDO implementation is responsible for implementing such polymorphic references on top of the underlying datastore, including a relational datastore. If a field is declared to be a reference to a transient class, and you assign a reference to an instance of a subclass that is persistent, the instance is not stored, because the field's declared type is not persistent.
You can use fields of Object and interface types. You can assign a reference to an instance of any class to an Object field, and an instance of any class implementing an interface can be assigned to an interface. You can also use interface inheritance in your model. Interface fields are transient by default, so you need to declare the field persistent explicitly in your metadata. We recommend you assign only instances of types supported by JDO to Object and interface fields. If an implementation restricts the type of instance that can be assigned to such a field, it will throw a ClassCastException when an incorrect assignment is made.
You can use a collection to represent multiple values of a given type or to represent to-many relationships among classes in an object model. Table 4-3 lists the Collection and Set collection interfaces and the HashSet collection class from the java.util package that are available in all JDO implementations. Additional collection classes that are optional in JDO are listed with their associated option property name. If an implementation supports the collection, it will return the collection's associated property string when you call PersistenceManagerFactory.supportedOptions( ).
Interface in the java.util package |
Class implementing the interface in the java.util package |
JDO option property |
---|---|---|
Collection |
portable (all implementations) | |
Set |
portable (all implementations) | |
HashSet |
portable (all implementations) | |
Hashtable |
javax.jdo.option.Hashtable | |
TreeSet |
javax.jdo.option.TreeSet | |
List |
javax.jdo.option.List | |
ArrayList |
javax.jdo.option.ArrayList | |
LinkedList |
javax.jdo.option.LinkedList | |
Vector |
javax.jdo.option.Vector | |
Map |
javax.jdo.option.Map | |
HashMap |
javax.jdo.option.HashMap | |
TreeMap |
javax.jdo.option.TreeMap |
You use a collection element to specify a collection's characteristics in the metadata. By default, collection-typed fields are persistent with an Object element type. You use the collection element's element-type attribute to specify the collection's element type. Specifying the element type is not required, but we recommend you specify it. The type name you specify uses Java's rules for naming: if no package is provided in the name, the package is assumed to be the same package as the enclosing persistent class in the metadata. Inner classes are identified with the $ marker. At some point, the Java language may allow you to specify a collection's element type directly when you declare the collection in your Java code, in which case this metadata will no longer be necessary.
A Map maintains a set of key-value pairs; both the key and value have a type. You use a map element to specify the characteristics of map's keys and values in the metadata. By default, map-typed fields are persistent and their key and value types are Object. You can use the map element's key-type and value-type attributes to specify a more specific type. As with collections, Java's rules for naming apply if the package is not provided, and inner classes can be identified with the $ marker.
We encourage you to specify the types of collection elements and the keys and values of Maps. Some implementations use a far less efficient means of accessing the elements if you do not specify the type.
Array fields are optional in JDO. The JDO javax.jdo.option.Array option property indicates whether an implementation supports them. You should not share a specific array among several persistent instances. The JDO specification does not state whether multidimensional arrays are supported. Support for multidimensional arrays varies among implementations.
A field's type and modifiers in a Java class declaration determine whether it is persistent by default. You can also override the default persistence of a field by declaring it as persistent or transient in the metadata.
Some fields cannot be persistent. A field declared in Java to be static or final is always transient. A static field has only one value; the field is associated with the class itself and shared by all instances. A final field has one value per instance. But a final field is initialized once by the constructor and its value can never be changed once the instance is constructed. Each constructor may initialize a final field differently. JDO implementations call the no-arg constructor to create an instance you access from the datastore. The field values from the datastore are set after the no-arg constructor is called. Thus, it is not possible for the JDO implementation to manage a final field's persistent state in memory.
Fields of the following types are persistent by default:
Any type identified in Table 4-2 or Table 4-3 (except for Object)
References to instances of persistent classes
Fields of the following types are transient by default:
References to transient application classes
References to system classes defined in JDK packages (unless supported in JDO)
Interface references
Object references
Though interface and Object references are transient by default, you can still declare them to be persistent in the metadata.
Java's transient modifier is used to specify whether a field and the object graph it may reference should be serialized. By default, a field declared transient in a Java class declaration is transient from a JDO perspective, but you can override this in the metadata. You can use the field element's persistence-modifier attribute to specify whether a field is persistent, by giving it one of the following values:
The field is persistent.
The field is transient.
The field is a transactional field, which is a transient field that has transactional behavior. Chapter 12 covers transactional fields.
So, a transient field in Java (specified via the transient modifier in the Java class declaration) is distinct from a transient field in JDO. If you declare a field in a Java class declaration with the transient modifier, it can be transient or persistent in JDO; and if a field does not have the Java transient modifier, it can also be transient or persistent, depending on the field's persistence-modifier attribute. If you do not specify the persistence-modifier attribute in the metadata, its default value is based on the field's type and modifiers, as defined in the Java class declaration.
There is no restriction on the type of a transient field. Transient fields are managed entirely by the application, not by the JDO implementation. A JDO implementation calls the no-arg constructor to instantiate an instance when the application accesses it from the datastore. You can define the default constructor to initialize transient and final fields. The InstanceCallbacks interface can also be used to manage the state of transient fields; this is covered in Chapter 12.
Persistent and transactional fields are also referred to as managed fields, since the JDO implementation manages their state. Figure 4-5 illustrates which kinds of fields are managed and which are transient.
A class's metadata cannot specify characteristics for any field it inherits from a superclass, so a subclass cannot alter the persistence of an inherited field. Therefore, a field identified as persistent by the class's metadata is persistent in all subclasses; if it is transactional, it is transactional in all subclasses, and if it is transient, it is transient in all subclasses.
Consider class E, contained in the inheritance hierarchy depicted in Figure 4-1. E is a persistent class that extends the transient class B. B extends the persistent class A. For any instance of B, E, or any class extending E, the fields of B are transient, and you cannot make them persistent in the metadata unless you make B a persistent class.
Of course, you can declare a class with a field that has the same name as a field in a superclass. Even though the field name is the same, these are two different fields. Therefore, you can have different values for their persistence-modifier attribute.
Now we can present the complete metadata for our Media Mania model, including the additional metadata we have covered:
<?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" > <field name="content" > <collection element-type="MediaContent"/> </field> </class> <class name="MediaContent" > <field name="mediaItems" > <collection element-type="com.mediamania.store.MediaItems"/> </field> </class> <class name="Movie" persistence-capable-superclass="MediaContent"> <field name="cast" > <collection element-type="Role"/> </field> </class> <class name="MediaPerson" > <field name="actingRoles" > <collection element-type="Role"/> </field> <field name="moviesDirected" > <collection element-type="Movie"/> </field> </class> <class name="Game" persistence-capable-superclass="MediaContent" /> <class name="Role" /> </package> <package name="com.mediamania.store" > <class name="MediaItem" > <field name="rentalItems"> <collection element-type="RentalItem"/> </field> </class> <class name="RentalItem"/> <class name="Customer" > <field name="currentRentals"> <collection element-type="Rental"/> </field> <field name="transactionHistory"> <collection element-type="Transaction"/> </field> </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>
We specified each collection's element type in the model. The mediaItems field in MediaContent is the only collection whose element type is a class in a different package, so we specified the full package name.