Skip Headers
Oracle® TopLink Developer's Guide
10g Release 3 (10.1.3.1.0)

Part Number B28218-01
Go to Documentation Home
Home
Go to Book List
Book List
Go to Table of Contents
Contents
Go to Index
Index
Go to Feedback page
Contact Us

Go to previous page
Previous
Go to next page
Next
View PDF

23 Understanding Descriptors

TopLink uses descriptors to store the information that describes how an instance of a particular class can be represented by a data source. Descriptors own mappings that associate class instance variables with a data source and transformation routines that are used to store and retrieve values. As such, the descriptor acts as the connection between a Java object and its data source representation.

This chapter includes information on the following:

Descriptor Types

Table 23-1 lists the descriptor types you use to describe the classes in your object model and classifies them as basic or advanced.

Table 23-1 TopLink Descriptor Types

Descriptor Type Description Type TopLink Workbench Java

"Relational Descriptors"


Describes Java objects that you map to tables in a relational database. Applicable to all relational databases that TopLink supports.

Basic

Supported
Supported

"Object-Relational Descriptors"


Describes Java objects that you map to tables in a relational database that provides special database data types that correspond more closely to object types. Applicable only to the relational databases that TopLink supports that provide these special data types.

Advanced

Unsupported
Supported

"EIS Descriptors"


Describes Java objects that you map to an EIS data source by way of a J2C adapter.

Basic

Supported
Supported

"XML Descriptors"


Describes Java objects that you map, in memory, to complex types in XML documents defined by an XML schema document (XSD).

Basic

Supported
Supported

For more information, see the following:

Descriptor Concepts

This section introduces descriptor concepts unique to TopLink, including the following:

Descriptor Architecture

A descriptor stores all the information describing how an instance of a particular object class can be represented in a data source.

TopLink descriptors contain the following information:

  • The persistent Java class it describes and the corresponding data source (database tables, XML complex type, or EIS interaction)

  • A collection of mappings, which describe how the attributes and relationships for that class are stored in the database

  • The primary key information (or equivalent) of the data source

  • A list of query keys (or aliases) for field names

  • Information for sequence numbers

  • A set of optional properties for tailoring the behavior of the descriptor, including support for caching refresh options, identity maps, optimistic locking, the event manager, and the query manager

There is a descriptor type for each data source type that TopLink supports. In some cases, multiple descriptor types are valid for the same data source type. The type of descriptor you use determines the type of mappings that you can define.

Table 23-2 summarizes the relationship between project, descriptor, and mappings.

Descriptors and Inheritance

Inheritance describes how a derived (child) class inherits the characteristics of its superclass (parent). You can use descriptors to describe the inheritance relationships between classes in relational, EIS, and XML projects.

In the descriptor for a child class, you can override mappings that have been specified in the descriptor for a parent class, or map attributes that have not been mapped at all in the parent class descriptor.

For more information, see "Understanding Descriptors and Inheritance".

Descriptors and EJB

You can use descriptors to describe the characteristics of entity beans with container-managed or bean-managed persistence.

When mapping enterprise beans, you create a descriptor for the bean class: you do not create a descriptor for the local interface, remote interface, home class, or primary key class.

When using TopLink Workbench, you must define the project with the correct entity bean type (such as entity beans with container-managed or bean-managed persistence) and import the ejb-jar.xml file for the beans into the TopLink Workbench project.

For CMP projects, you use the ejb-jar.xml file to define the bean's mapped attributes. A descriptor of a bean with container-managed persistence contains a CMP policy used to configure CMP-specific options.


Note:

For EJB 3.0 projects, you can use annotations to define the bean's mapped attributes.

This section describes the following:

Nondeferred Changes

By default, TopLink defers all changes until commit time: this is the most efficient approach that produces the least number of data source interactions.

Alternatively, you can configure an entity bean's descriptor for nondeferred changes. This means that as you change the persistent fields of the entity bean, TopLink CMP modifies the relational schema immediately.

Using nondeferred changes, you can achieve backward compatibility with the native behavior of some EJB containers (such as OC4J). You can also accommodate advanced applications that rely on the database and entity changes being synchronized for such things as triggers or stored procedures based on transient state within the transaction, deletion and creation of rows with the same primary key, or other complex queries that depend on transient transaction state.

Nondeferred changes have the disadvantage of being the least efficient approach: they produce the greatest number of data source interactions.

When you configure TopLink CMP to support nondeferred changes, TopLink will continue to handle constraints for mapped relationships among entity beans with the same deferral setting. However, you are responsible for handling any errors that result from making changes to a class that is not deferred, but related to a class that is deferred when a constraint exists between these two classes.


Note:

When you configure a descriptor for nondeferred changes, TopLink CMP does not apply nondeferred changes to dependent objects. Dependent objects are subject to default deferred changes: the relational schema is not modified until commit.

For more information, see "Configuring a Descriptor With EJB Information".

Creating a New Entity Bean and ejbCreate / ejbPostCreate Methods

When you create a new entity bean, by default, the bean's life cycle can be thought of as follows:

  1. ejbCreate method:

    After the insert, the EJB container retrieves the primary key allocated by the database for the created instance.

    For a relational project:

    1. INSERT INTO ...

    2. SELECT FROM ...

    For an EIS project:

    1. Write object ...

    2. Find object ...

  2. ejbPostCreate method:

    The EJB container updates container-managed relationship (CMR) fields. The EJB container needs the primary key obtained in the ejbCreate method.

    For a relational project:

    1. UPDATE SET ...

    For an EIS project:

    1. Write object ...

However, if you have non-null foreign key constraints in your database, doing a data source modification after the ejbCreate method executes can cause problems. To get around this, some application servers (such as OC4J) allow you to create new objects after the ejbPostCreate method executes, and rely on the container to resolve the foreign key constraint.

For more information, see "Configuring a Descriptor With EJB Information".

Inheritance

TopLink allows you to configure inheritance for CMP descriptors, with some reservations.

For more information, see "Inheritance and EJB".

Fetch Groups

By default, when you execute an object-level read query for a particular object class, TopLink returns all the persistent attributes mapped in the object's descriptor. With this single query, all the object's persistent attributes are defined, and calling their get methods returns the value directly from the object.

When you are interested in only some of the attributes of an object, it may be more efficient to return only a subset of the object's attributes using a fetch group with which you can define a subset of an object's attributes and associate the fetch group with either a ReadObjectQuery or ReadAllQuery query.

For more information, see the following:

Amendment and After-Load Methods

Using TopLink Workbench, you can associate a static Java method that is called when a descriptor is loaded at run time. This method can amend the run-time descriptor instance through the descriptor Java code API. Use this method to make some advanced configuration options that may not be currently supported by TopLink Workbench.Descriptors can only be modified before the session has been connected; descriptors should not be modified after the session has been connected.

For more information, see "Configuring Amendment Methods".

Descriptors and Aggregation

Two objects–a source (parent or owning) object and a target (child or owned) object–are related by aggregation if there is a strict one-to-one relationship between them, and all the attributes of the target object can be retrieved from the same data source representation as the source object. This means that if the source object exists, then the target object must also exist, and if the source object is destroyed, then the target object is also destroyed.

In this case, the descriptors for the source and target objects must be designated to reflect this relationship as follows:

Aggregate and Composite Descriptors in Relational Projects

In a relational project, you can designate the descriptor as an aggregate (see "Relational Aggregate Descriptors").

This lets you configure an aggregate mapping (see Chapter 43, "Configuring a Relational Aggregate Object Mapping") to associate data members in the target object with fields in the source object's underlying database tables.

When you designate a relational descriptor as an aggregate, TopLink lets you specify a mapping type for each field in the target class, but defers associating the field with a database table until you configure the aggregate object mapping in the source descriptor. In other words, the target class descriptor defines how each target class field is mapped, but the source class descriptor defines where each target class field is mapped. This lets you share an aggregate object among many parent descriptors mapped to different tables.

When you designate a relational descriptor as an aggregate, you tell TopLink that the class will be a target of an aggregate object mapping, and this ensures that the TopLink runtime handles the target class as follows:

  • It inserts, updates, and deletes the target class in parallel with its source class.

  • It does not cache the target class on its own; instead, it caches the target class as part of its source class.

  • It does not allow the target class to be read, written, deleted, or registered in a unit of work.

When working with aggregate relational descriptors, consider the following:

For more information, see "Configuring a Relational Descriptor as a Class or Aggregate Type".

Relational Aggregates and Nesting

TopLink supports nested aggregates. In Figure 23-1 source class HockeyPlayer is a normal nonaggregate class descriptor. It owns target class Info which is designated as an aggregate. The Info class itself owns target classes PersonalInfo and TeamInfo which are each designated as aggregates.

Figure 23-1 Nested Aggregates

Description of Figure 23-1 follows
Description of "Figure 23-1 Nested Aggregates"

In EJB 3.0, an aggregate is known as an embeddable. In the EJB 3.0 specification, an embeddable may not contain another embeddable (that is, the EJB 3.0 specification does not support nested aggregates).

However, if you deploy a TopLink-enabled EJB 3.0 application with persistence to OC4J, you can take advantage of a TopLink extension of the EJB 3.0 specification to configure nested embeddables. Note that if you do so, your application will not be strictly EJB 3.0-compliant. Example 23-1 shows the classes from Figure 23-1 using EJB 3.0 annotations to take advantage of the TopLink extension of the EJB 3.0 specification to allow Info (an embeddable) to own embeddables TeamInfo and PersonalInfo.

Example 23-1 Nested Embeddables

public class HockeyPlayer implements Serializable {
    private int playerId;
    private Info Info;
    private String lastName;
    private String firstName;
    ...
    @Embedded
    public Info getInfo() {
        return Info;
    }
}
 
@Embeddable
public class Info implements Serializable {
    TeamInfo teamInfo; // TopLink extension of EJB 3.0 allows Embeddable with Embeddable
    PersonalInfo personalInfo;
    
    public Info() {}
    
    @Embedded
    public PersonalInfo getPersonalInfo() {
        return personalInfo;
    }
    
    public void setPersonalInfo(PersonalInfo personalInfo) {
        this.personalInfo = personalInfo;
    }
    
    @Embedded
    public TeamInfo getTeamInfo() {
        return teamInfo;
    }
    
    public void setTeamInfo(TeamInfo teamInfo) {
        this.teamInfo = teamInfo;
    }
}
 
@Embeddable
public class PersonalInfo implements Serializable {
    private int age;
    private double weight;
    private double height;
    ...
}
 
@Embeddable
public class TeamInfo implements Serializable {
    private String position;
    private int jerseyNumber;
    private HockeyTeam hockeyTeam;
    ...
}

Relational Aggregates and Inheritance

You can configure inheritance for a relational descriptor designated as an aggregate (see "Descriptors and Inheritance"), however, in this case, all the descriptors in the inheritance tree must be aggregates. Aggregate and class descriptors cannot exist in the same inheritance tree.

Relational Aggregates and EJB

You can use relational aggregate descriptors in an EJB project, but you cannot configure EJB information for a relational descriptor designated as an aggregate (see "Descriptors and EJB").

For more information on using relational aggregates and EJB 3.0, see "Relational Aggregates and Nesting".

Root and Composite Descriptors in EIS Projects

In an EIS project, you can designate the descriptor as a composites (see "EIS Composite Descriptors").

The type of EIS mapping you whish to create will determine whether you configure an EIS descriptor as a composite or root (see "Composite and Reference EIS Mappings").

For more information, see "Configuring an EIS Descriptor as a Root or Composite Type".

You cannot configure EJB information for an EIS descriptor designated as an composite (see "Descriptors and EJB").

You can configure inheritance for an EIS descriptor designated as a composite (see "Descriptors and Inheritance"), however, in this case, all the descriptors in the inheritance tree must be composites. Composite and root descriptors cannot exist in the same inheritance tree.

Composite Descriptors in XML Projects

In an XML project, descriptors are always composites.

Because XML descriptors are always composites, you can configure inheritance for an XML descriptor without considering its type (see "Descriptors and Inheritance").

Descriptor Event Manager

In relational and EIS projects, TopLink raises various instances of DescriptorEvent (see Table 25-26 and Table 25-28) during the persistence life cycle. Each descriptor owns an instance of DescriptorEventManager that is responsible for receiving these events and dispatching them to the descriptor event handlers registered with it.

Using a descriptor event handler, you can execute your own application specific logic whenever descriptor events occur, allowing you to take customized action at various points in the persistence life-cycle. For example, using a descriptor event handler, you can do the following:

  • Synchronize persistent objects with other systems, services, and frameworks.Maintain nonpersistent attributes of which TopLink is not aware.

  • Notify other objects in the application when the persistent state of an object changes.

  • Implement complex mappings or optimizations not directly supported by TopLink mappings.

For more information, see the following:

Descriptor Query Manager

Each relational and EIS descriptor provides an instance of DescriptorQueryManager that you can use to configure the following:

For more information on using the query manager, see "Descriptor Query Manager Queries".

Descriptors and Sequencing

An essential part of maintaining object identity is managing the assignment of unique values (that is, a specific sequence) to distinguish one object instance from another. For more information, see "Projects and Sequencing".

Sequencing options you configure at the project (or session) level determine the type of sequencing that TopLink uses. In a CMP project, you typically configure the sequence type at the project level (see "Configuring Sequencing at the Project Level"). In a non-CMP project, you can use session-level sequence configuration to override project-level sequence configuration, on a session-by-session basis, if required (see "Configuring Sequencing at the Session Level").

After configuring the sequence type, for each descriptor's reference class, you must associate one attribute, typically the attribute used as the primary key (see "Configuring Primary Keys"), with its own sequence (see "Configuring Sequencing at the Descriptor Level").

Descriptors and Locking

You can configure a descriptor with any of the following locking policies to control concurrent access to a domain object:

  • Optimistic–All users have read access to the data. When a user attempts to make a change, the application checks to ensure the data has not changed since the user read the data (see "Optimistic Version Locking Policies" and "Optimistic Field Locking Policies").

  • Pessimistic–The first user who accesses the data with the purpose of updating it locks the data until completing the update (see "Pessimistic Locking Policy").

  • No locking–The application does not prevent users overwriting each other's changes.

Oracle recommends using optimistic locking for most types of applications to ensure that users do not overwrite each other's changes.

For more information, see the following:

Default Root Element

You configure EIS root descriptors ("Configuring Default Root Element") and XML descriptors ("Configuring Default Root Element") with a default root element so that the TopLink runtime knows the data source data type associated with the class the descriptor describes.


Note:

The undefined document root element of a referenced object is ignored during marshalling with an any collection mapping and object mapping.

This section describes what a default root element is and how TopLink uses it.

Consider the Customer and Address classes and their mappings shown in Example 23-2.

Example 23-2 Customer and Address Classes

Class: Customer
Default Root: customer
Attributes and Mappings:
    name:String                Drect Mapping to                name/text()
    billingAddress:Address     Composite Object Mapping to     billing-address
    shippingAddress:Address    Composite Object Mapping to     shipping-address

Class: Address
Default Root: address
Attributes and Mappings:
    street:String              Direct Mapping to               street/text()
    city:String                Direct Mapping to               city/text()

These classes correspond to the XML schema shown in Example 23-3.

Example 23-3 Customer and Address Schema

<xsd:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
    <xsd:complexType name="address-type">
        <xsd:sequence>
            <element name="street" type="xsd:string"/>
            <element name="city" type="xsd:string"/>
        </xsd:sequence>
    </xsd:complexType>
    <xsd:element name="customer" type="customer-type"/>
    <xsd:complexType name="customer-type">
        <xsd:sequence>
            <xsd:element name="name" type="xsd:string"/>
            <xsd:element name="billing-address" type="address-type"/>
            <xsd:element name="shipping-address" type="address-type"/>
        </xsd:sequence>
    </xsd:complexType>
</xsd:schema>

When an instance of the Customer class is persisted to XML, the TopLink runtime performs the following:

  1. Gets the default root element.

    The Customer class instance corresponds to the root of the XML document. The TopLink runtime uses the default root element specified on the descriptor (customer) to start the XML document. TopLink then uses the mappings on the descriptor to marshal the object's attributes:

    <customer>
        <name>…</name>
    </customer>
    
    
  2. When the TopLink runtime encounters an object attribute such as billingAddress, it checks the mapping associated with it to determine with what element (billing-address) to continue:

    <customer>
        <name>…</name>
        <billing-address/>
    </customer>
    
    

    The TopLink runtime checks the mapping's reference descriptor (Address) to determine what attributes to persist:

    <customer>
        <name>…</name>
        <billing-address>
            <street>…</street>
            <city>…</city>
        </billing-address>
    </customer>
    

Relational Descriptors

Relational descriptors describe Java objects that you map to tables in a relational database. You use them in relational projects (see "Relational Projects").

Using relational descriptors in a relational project, you can configure relational mappings (see "Relational Mapping Types").

For more information, see the following:

Object-Relational Descriptors

The object-relational paradigm extends traditional relational databases to include object-oriented functions. Oracle, IBM DB2, Informix, and other DBMS databases allow users to store, access, and use complex data in more sophisticated ways.The object-relational standard is an evolving standard concerned mainly with extending the database data structures and SQL (SQL 3).

Object-relational descriptors describe Java objects that you map to special relational database types that correspond more closely to object types. Using these special object-relational database types can simplify mapping objects to relational database tables. Not all relational databases support these special object-relational database types.

Using object-relational descriptors in a relational project, you can configure object-relational mappings to these special object-relational database data types (see "Object-Relational Mapping Types").

For more information, see the following:

EIS Descriptors

EIS descriptors describe Java objects that you map to an EIS data source by way of a J2C adapter.

Using EIS descriptors in an EIS project created with TopLink Workbench, you can configure EIS mappings (see "EIS Mapping Types") to XML records.

Using EIS descriptors in an EIS project that you create in Java, you can configure EIS mappings to any supported EIS record type: XML, mapped, or indexed.

For more information, see the following:

XML Descriptors

XML descriptors describe Java objects that you map to simple and complex types defined by an XML schema document (XSD).

Using XML descriptors in an XML project, you can configure XML mappings (see "XML Mapping Types"), in memory, to XML elements defined by an XSD.

For more information, see the following:

Understanding Descriptors and Inheritance

Inheritance describes how a derived class inherits the characteristics of its superclass. You can use descriptors to describe the inheritance relationships between classes in relational, EIS, and XML projects.

Figure 23-2 illustrates the Vehicle object model–a typical Java inheritance hierarchy. The root class Vehicle contains two branch classes: FueledVehicle and NonFueledVehicle. Each branch class contains a leaf class: Car and Bicycle, respectively.

Figure 23-2 Example Inheritance Hierarchy

Description of Figure 23-2 follows
Description of "Figure 23-2 Example Inheritance Hierarchy "

TopLink recognizes the following three types of classes in an inheritance hierarchy:

  1. The root class stores information about all instantiable classes in its subclass hierarchy. By default, queries performed on the root class return instances of the root class and its instantiable subclasses. However, the root class can be configured so queries on it return only instances of itself, without instances of its subclasses.

    For example, the Vehicle class in Figure 23-2 is a root class.

  2. Branch classes have a persistent superclass and also have subclasses. By default, queries performed on the branch class return instances of the branch class and any of its subclasses. However, as with the root class, the branch class can be configured so queries on it return only instances of itself without instances of its subclasses.

    For example, the FueledVehicle class in Figure 23-2 is a branch class.

  3. Leaf classes have a persistent superclass in the hierarchy but do not have subclasses. Queries performed on the leaf class can only return instances of the leaf class.

    For example, the Car class in Figure 23-2 is a leaf class.

In the descriptor for a child class, you can override mappings that have been specified in the descriptor for a parent class, or map attributes that have not been mapped at all in the parent class descriptor.

This section includes information on the following topics:

For more information about configuring inheritance for a parent (root) class descriptor, see "Configuring Inheritance for a Parent (Root) Descriptor".

For more information about configuring inheritance for a child (branch or leaf) class descriptor, see "Configuring Inheritance for a Child (Branch or Leaf) Class Descriptor".

Specifying a Class Indicator

When configuring inheritance, you configure the root class descriptor with the means of determining which subclasses it should instantiate.

You can do this in one of the following ways:


Note:

All leaf classes in the hierarchy must have a class indicator and they must have the same type of class indicator (field or class extraction method).

Using Class Indicator Fields

You can use a persistent attribute of a class to indicate which subclass should be instantiated. For example, in a relational descriptor, you can use a class indicator field in the root class table. The indicator field should not have an associated direct mapping unless it is set to read-only.


Note:

If the indicator field is part of the primary key, define a write-only transformation mapping for the indicator field (see Chapter 45, "Configuring a Relational Transformation Mapping").

You can use strings or numbers as values in the class indicator field.

The root class descriptor must specify how the value in the class indicator field translates into the class to be instantiated.

One approach is to configure the root class descriptor with a class indicator dictionary: a collection of key-values that associates a simple key, stored in the class indicator field, with a class to instantiate. Table 23-3 illustrates the class indicator dictionary for the Vehicle class's subclasses as shown in Figure 23-2.

Table 23-3 Class Indicator Dictionary for the Vehicle Class

Key Value

F

FueledVehicle

N

NonFueledVehicle

C

Car

B

Bicycle


Another approach is to simply use the class name itself as the value stored in the class indicator field. This avoids having to define unique indicators for each class at the expense of a slightly larger key value (depending on the length of your class names).

Using Class Extraction Methods

You can define a Java method to compute the class indicator based on any available information in the object's data source record. Such a method is called a class extraction method.

Using a class extraction method, you do not need to include an explicit class indicator field in your data model and you can handle relationships that are too complex to describe using class indicator fields.

A class extraction method must have the following characteristics:

  • it must be defined on the root descriptor's class

  • it must be static

  • it must take a Record as an argument

  • it must return the java.lang.Class object to use for the Record passed in

You may also need to define only-instances and with-all-subclasses expressions (see "Specifying Expressions for Only-Instances and With-All-Subclasses").

For example, Table 23-4 lists the rows in the EMPLOYEE table. The Employee class is the base class. Director, Manager, Programmer, and TechWriter classes each derive from the Employee class. However, in your application, instances of Manager, Programmer, and TechWriter classes must be represented as Employee instances and instances of Director must be represented as Director instances. Because there is no a one-to-one correspondence between class and JOB_TYPE field value, the JOB_TYPE field alone cannot serve as a class indicator field (see "Using Class Indicator Fields"). To resolve this issue, you could use the class extraction method shown in Example 23-4.

Table 23-4 EMPLOYEE Table

ID NAME JOB_TYPE JOB_TITLE

732

Bob Jones

1

Manager

733

Sarah Smith

3

Technical Writer

734

Ben Ng

2

Director

735

Sally Johnson

3

Programmer


Example 23-4 Class Extraction Method

...
// If the JOB_TYPE field value in record equals 2, return the Director class.
// Return the Employee class for all other JOB_TYPE field values

public static Class getClassFromRecord(Record record)
{
    if (record.get("JOB_TYPE").equals(new Integer(2))
    {
        return Director.class;
    }
    else
    {
        return Employee.class;
    }
}

When configuring inheritance using a class extraction method, Oracle TopLink does not generate SQL for queries on the root class.

Specifying Expressions for Only-Instances and With-All-Subclasses

If you use a class extraction method (see "Using Class Extraction Methods"), you must provide TopLink with expressions to correctly filter sibling instances for all classes that share a common table (see "Configuring Inheritance Expressions for a Parent (Root) Class Descriptor").

Inheritance and Primary Keys (Relational and EIS Only)

For relational and EIS projects, TopLink assumes that all of the classes in an inheritance hierarchy have the same primary key, as set in the root descriptor. Child descriptors associated with data source representations that have different primary keys must define the mapping between the root primary key and the local one.

Single and Multi-Table Inheritance (Relational Only)

In a relational project, you can map your inheritance hierarchy to a single table ("Single Table Inheritance") or to multiple tables ("Multitable Inheritance"). Use these options to achieve the balance between storage efficiency and access efficiency that is appropriate for your application.

Single Table Inheritance

In this example, you store classes with multiple levels of inheritance in a single table to optimize database access speeds.

The entire inheritance hierarchy shown in Figure 23-2 can share the same table, as in Figure 23-3. The FueledVehicle and NonFueledVehicle subclasses can share the same table even though FueledVehicle has some attributes that NonFueledVehicle does not. The NonFueledVehicle instances waste database resources because the database must still allocate space for the unused portion of its row. However, this approach saves on accessing time because there is no need to join to another table to get the additional FueledVehicle information.

As Figure 23-3 shows, this approach uses a class indicator field. For more information, see "Specifying a Class Indicator".

Figure 23-3 Inheritance Using a Superclass Table with Optional Fields

Description of Figure 23-3 follows
Description of "Figure 23-3 Inheritance Using a Superclass Table with Optional Fields"

Multitable Inheritance

In this example, you store classes with multiple levels of inheritance in multiple tables to optimize database storage space.

In the inheritance hierarchy shown in Figure 23-2, for subclasses that require additional attributes, you use multiple tables instead of a single superclass table. This optimizes storage space because there are no unused fields in the database. However, this may affect performance because TopLink must read from more than one table before it can instantiate the object. TopLink first looks at the class indicator field (see "Specifying a Class Indicator") to determine the class of object to create, then uses the descriptor for that class to read from the subclass tables.

Figure 23-4 illustrates the TopLink implementation of the FUELEDVHCL, CAR, and BICYCLE tables. All objects are stored in the VEHICLE table. FueledVehicle, Car, and Bicycle information are also stored in secondary tables. Note that because the NonFueledVehicle class does not hold any attributes or relationships, it does not need a secondary table.

Figure 23-4 Inheritance Using Separate Tables for Each Subclass

Description of Figure 23-4 follows
Description of "Figure 23-4 Inheritance Using Separate Tables for Each Subclass"


Note:

. In general, using multitable inheritance is inefficient because it can require excessive joins and multiple table fetching.

Inheritance View

If a root or branch inheritance descriptor has subclasses that span multiple tables, you can configure a database view to optimize the performance of queries against the parent descriptor by outer-joining all of the subclass tables. This allows TopLink to fetch all of the subclass instances in one query, instead of multiple queries. It also allows queries for the parent class that use cursors or ordering.

You must define the view on the database as a database view that outer-joins all of the subclass tables. For more information, see "Configuring Reading Subclasses on Queries".

Aggregate and Composite Descriptors and Inheritance

You can designate relational descriptors as aggregates, and EIS descriptors as composites. XML descriptors are always composites (see "Descriptors and Aggregation").

When configuring inheritance for a relational aggregate descriptor, all the descriptors in the inheritance tree must be aggregates. The descriptors for aggregate and non-aggregate classes cannot exist in the same inheritance tree.

Similarly, when configuring inheritance for an EIS composite descriptor, all the descriptors in the inheritance tree must be composites. The descriptors for composite and noncomposite classes cannot exist in the same inheritance tree.

When configuring inheritance for an XML descriptor, because all XML descriptors are composites, descriptor type does not restrict inheritance.

Inheritance and EJB

Although inheritance is a standard tool in object-oriented modeling, the EJB specifications prior to 3.0 contain only general information regarding inheritance. You should fully understand this information before implementing EJB inheritance. Be aware of the fact that future EJB specifications may dictate inheritance guidelines not supported by all application servers.

Understanding Descriptors and Locking

This section describes the various types of locking policy that TopLink supports, including the following:

For more information, see "Configuring Locking Policy".

Optimistic Version Locking Policies

With optimistic locking, all users have read access to the data. When a user attempts to make a change, the application checks to ensure the data has not changed since the user read the data.

Optimistic version locking policies enforce optimistic locking by using a version field (also known as a write-lock field) that you provide in the reference class that TopLink updates each time an object change is committed.

TopLink caches the value of this version field as it reads an object from the data source. When the client attempts to write the object, TopLink compares the cached version value with the current version value in the data source in the following way:

  • If the values are the same, TopLink updates the version field in the object and commits the changes to the data source.

  • If the values are different, the write operation is disallowed because another client must have updated the object since this client initially read it.

TopLink provides the following version-based optimistic locking policies:

  • VersionLockingPolicy: requires a numeric version field; TopLink updates the version field by incrementing its value by one.

  • TimestampLockingPolicy: requires a timestamp version field; TopLink updates the version field by inserting a new timestamp (this policy can be configured to get the time from the data source or locally; by default, the policy gets the time from the data source).


Note:

In general, Oracle recommends numeric version locking, because:
  • accessing the timestamp from the data source can cause a performance issue

  • time stamp locking is limited to the precision that the database stores for timestamps


Whenever any update fails because optimistic locking has been violated, TopLink throws an OptimisticLockException. This should be handled by the application when performing any database modification. The application must notify the client of the locking contention, refresh the object, and have the client reapply its changes.

You can choose to store the version value in the object as a mapped attribute, or in the cache. In three-tier applications, you typically store the version value in the object to ensure it is passed to the client when updated (see "Locking in a Three-Tier Application").

If you store the version value in the cache, you do not need to map it. If you do map the version field, you must configure the mapping as read-only (see "Configuring Read-Only Mappings").

To ensure that the parent object's version field is updated whenever a privately owned child object is modified, consider "Optimistic Version Locking Policies and Cascading".

When using optimistic version locking with the unit of work, consider "Using Optimistic Read Locking With forceUpdateToVersionField".

Optimistic Version Locking Policies and Cascading

If your database schema is such that both a parent object and its privately owned child object are stored in the same table, then if you update the child object, the parent object's version field will be updated.

However, if the parent and its privately owned child are stored in separate tables, then changing the child will not, by default, update the parent's version field.

To ensure that the parent object's version field is updated in this case, you can either manually update the parent object's version field (see "Using Optimistic Read Locking With forceUpdateToVersionField") or, if you are using a TimestampLockingPolicy, you can configure TopLink to automatically cascade the child object's version field update to the parent (see "Configuring Optimistic Locking Policy Cascading").

After you enable optimistic version locking cascading, when a privately owned child object is modfied, TopLink will traverse the privately owned foreign reference mappings, updating all the parent objects back to the root.

Optimistic version locking cascading is only applied if the child object is registered in a unit of work.

TopLink supports optimistic version locking cascading for:

  • object changes in privately owned one-to-one and one-to-many mappings

  • relationship changes (adding or removing) in the following collection mappings (privately owned or not):

    • direct collection

    • one-to-many

    • many-to-many

    • aggregate collection

Consider the example object graph shown in Figure 23-5

Figure 23-5 Optimistic Version Locking Policies and Cascading Example

Description of Figure 23-5 follows
Description of "Figure 23-5 Optimistic Version Locking Policies and Cascading Example"

In this example, ObjectA privately owns ObjectB, and ObjectB privately owns ObjectC, and ObjectC privately owns ObjectD.

Suppose you register ObjectB in a unit of work, modify an ObjectB field, and commit the unit of work. In this case, ObjectB checks the cache for ObjectA and, if not present, queries the database for ObjectA. ObjectB then notifies ObjectA of its change. ObjectA forces an update on its version optimistic locking field even though it has no changes to its corresponding table.

Suppose you register ObjectA in a unit of work, access its ObjectB to access its ObjectC to access its ObjectD, modify an ObjectD field, and commit the unit of work. In this case, ObjectD notifies ObjectC of its changes. ObjectC forces an update on its version optimistic locking field even though it has no changes to its corresponding table. ObjectC then notifies ObjectB of the ObjectD change. ObjectB then notifies ObjectA of the ObjectD change. ObjectA forces an update on its version optimistic locking field even though it has no changes to its corresponding table.

Optimistic Locking and Rollbacks

With optimistic locking, use the UnitOfWork method commitAndResumeOnFailure (see "Resuming a Unit of Work After Commit") to rollback a locked object's value, if you store the optimistic lock versions in the cache.

If you store the locked versions in an object, you must refresh the objects (or their versions) on a failure. Alternatively, you can acquire a new unit of work on the failure and reapply any changes into the new unit of work.

Optimistic Field Locking Policies

Optimistic field locking policies enforce optimistic locking by using one or more of the fields that currently exist in the table to determine if the object has changed since the client read the object.

The unit of work caches the original state of the object when you first read the object or register it with the unit of work. At commit time, the unit of work compares the original values of the lock fields with their current values on the data source during the update. If any of the lock field's values have changed, an optimistic lock exception is thrown.

TopLink provides the following optimistic field locking policies:

  • AllFieldsLockingPolicy: For update and delete operations, TopLink compares all the fields of the object with all the fields in the data source. If the original value of any fields differ from that in the data source, the write operation is disallowed.

    For example, if you changed a customer's last name, TopLink might produce SQL like:

    UPDATE CUSTOMER SET LNAME='new last name' WHERE ID=7 AND LNAME='old last name' AND FNAME='Donald' AND B_DAY='1972' AND CREDIT_RATING='A+' AND EYE_COLOR='Blue'
    
    

    The main disadvantage of this field locking policy is that it is not the most efficient, especially if the changed object has many attributes.


    Note:

    This comparison is only on a per table basis. If an update operation is performed on an object that is mapped to multiple tables (multiple table inheritance), then only the changed fields for each table changed appear in the where clause.

  • ChangedFieldsLockingPolicy: For update operations, TopLink compares only the fields of the object that have changed with the corresponding fields in the data source. If the original value of any such field differs from that in the data source, the write operation is disallowed. TopLink does not make any field comparisons for deletes.

    The main advantage of this field locking policy is that it allows concurrent updates of different fields. For example, if one thread updates a customer's last name and another thread updates the same customer's credit rating, and you configure the Customer descriptor with ChangedFieldsLockingPolicy, then TopLink might produce SQL like:

    // Unit of Work 1
    UPDATE CUSTOMER SET LNAME='new name' WHERE ID=7 AND LNAME='old name'
    // Unit of Work 2
    UPDATE CUSTOMER SET CREDIT_RATING='B' WHERE ID=7 AND CREDIT_RATING='A+'
    
    
  • SelectedFieldsLockingPolicy: For update and delete operations, TopLink compares only the selected fields of the object with the corresponding fields in the data source. If the cached value of any such field differs from that in the data source, the write operation is disallowed.

    For example, if you select Customer attributes LNAME and CREDIT_RATING, then at run time, TopLink might produce SQL like:

    UPDATE CUSTOMER SET LNAME='new name' WHERE ID=7 AND LNAME='old name' AND CREDIT_RATING='A+'
    
    

Whenever any update fails because optimistic locking has been violated, TopLink throws an OptimisticLockException. This should be handled by the application when performing any database modification. The application must notify the client of the locking contention, refresh the object, and have the client reapply its changes.

When using field locking policies, a unit of work must be employed for updating the data source.


Note:

You cannot use an instance of FieldsLockingPolicy if you are using AttributeChangeTrackingPolicy (see "Attribute Change Tracking Policy").

Pessimistic Locking Policy

With pessimistic locking, the first user who accesses the data with the purpose of updating it locks the data until completing the update.

When using a pessimistic locking policy, you can configure the policy to either fail immediately or to wait until the read lock is acquired.

You can use a pessimistic locking policy only in a project with a container-managed persistence type (see "Configuring Persistence Type") and with descriptors that have EJB information (see "Configuring a Descriptor With EJB Information").

You can also use pessimistic locking (but not a pessimistic locking policy) at the query level (see "Configuring Named Query Options").

TopLink provides an optimization for pessimistic locking when this locking is used with entity beans with container-managed persistence: if you set your query to pessimistic locking and run the query in its own new transaction (which will end after the execution of the finder), then TopLink overrides the locking setting and does not append FOR UPDATE to the SQL. However, the use of this optimization may produce an undesirable result if the pessimistic lock query has been customized by the user with a SQL string that includes FOR UPDATE. In this case, if the conditions for the optimization are present, the query will be reset to nonpessimistic locking, but the SQL will remain the same resulting in the locking setting of the query conflicting with the query's SQL string. To avoid this problem, you can take one of the following two approaches:

  • Use EJB QL or a TopLink's expression (see Chapter 95, "Understanding TopLink Expressions") for the selection criteria. This will give TopLink control over the SQL generation.

  • Place the finder in a transaction to eliminate conditions for the optimization.

Locking in a Three-Tier Application

If you are building a three-tier application, in order to correctly lock an object, you must obtain the lock before the object is sent to client for editing.

Optimistic Locking in a Three-Tier Application

If you are using optimistic locking, you have the following two choices for locking objects correctly:

  1. Map the optimistic lock field in your object as not read-only and pass the version to the client on the read and back to the server on the update.

    You must define a non-read-only mapping for the version field and make the optimistic locking policy store the version value in the object, not the cache (in TopLink Workbench, this is done on the Locking tab by unchecking Store Version in Cache: see "Using TopLink Workbench").

    Ensure that the original version value is sent to the client when it reads the object for the update. The client must then pass the original version value back with the update information, and this version must be set into the object to be updated after it is registered/read in the new unit of work on the server.

  2. Hold the unit of work for the duration of the interaction with the client.

    Either through a stateful session bean, or in an HTTP session, store the unit of work used to read the object for the update for the duration of the client interaction.

    Your must read the object through this unit of work before passing it to the client for the update. This ensures that the version value stored in the unit of work cache or in the unit of work clone will be the original value.

    This same unit of work must be used for the update.

The first option is more commonly used, and is required if developing a stateless application.

Pessimistic Locking in a Three-Tier Application

If you are using pessimistic locking, you must use the unit of work to start a database transaction before the object is read. You must hold this unit of work and database transaction while the client is editing the object and until the client updates the object. You must use this same unit of work to update the object. If you are building a three-tier Web application (where it is not normally desirable to hold a database transaction open across client interactions), optimistic locking is normally more desirable than pessimistic locking (see "Optimistic Locking in a Three-Tier Application").

Understanding the Descriptor API

The descriptor API can be used to define, or amend TopLink descriptors through Java code. The descriptor API classes are mainly in the oracle.toplink.descriptors package. These include the following classes:

For object-relational, EIS, and XML projects, descriptor classes are in the oracle.toplink.objectrelational, oracle.toplink.eis, and oracle.toplink.ox packages, respectively.

This section describes the important descriptor classes in the Oracle TopLink Foundation Library, including:

Descriptor Inheritance Hierarchy

Example 23-5 illustrates the descriptor types that derive from class oracle.toplink.descriptors.ClassDescriptor.

Example 23-5 Descriptor Inheritance Hierarchy

class oracle.toplink.descriptors.ClassDescriptor
    class oracle.toplink.descriptors.RelationalDescriptor
        class oracle.toplink.objectrelational.ObjectRelationalDescriptor
    class oracle.toplink.eis.EISDescriptor
    class oracle.toplink.ox.XMLDescriptor