Skip Headers

Oracle Application Server TopLink Application Developer's Guide
10g (9.0.4)

Part Number B10313-01
Go To Documentation Library
Home
Go To Product List
Solution Area
Go To Table Of Contents
Contents
Go To Index
Index

Go to previous page Go to next page

3
Mapping

Mapping enables you to relate objects in your application to data in a database. This chapter describes how you can build mappings for Oracle Application Server TopLink-based applications. It includes descriptions of:

For more information about mappings, see also the Oracle Application Server TopLink Mapping Workbench User's Guide.

Introduction to Mapping Concepts

In an OracleAS TopLink application, you persist objects by storing, or mapping, information about them in a relational database. A mapping has three components:

Although OracleAS TopLink supports more complex mappings, most OracleAS TopLink classes map to a single database table that defines the type of information available in the class. Each object instantiated from a given class maps to a single row comprising the object's attributes, plus an identifier (the primary key) that uniquely identifies the object.

Figure 3-1 How Classes and Objects Map to a Database Table

Text description of ezmap.gif follows.

Text description of the illustration ezmap.gif

Figure 3-1 illustrates the simplest case in which:

OracleAS TopLink provides you with the tools to build these mappings, from the simple mappings illustrated in Figure 3-1, to complex mappings. OracleAS TopLink addresses the most difficult challenge for mapping--transforming a class or object into database table or row.

The following section describes the basic concepts that you must understand before moving on to the more in-depth information in this chapter, and introduces some of the more complex issues that are part of mapping.

Persistent Entities

Persistent entities are entities that survive, or persist, beyond the scope of a given transaction. A key feature of OracleAS TopLink is its ability to persist objects and entities in an application by mapping them to a database.

Metadata Model

OracleAS TopLink implements a metadata model, in which OracleAS TopLink uses metadata to define how objects and classes map to tables or rows, as well how tables and rows map to objects and classes. OracleAS TopLink uses the metadata, contained in the descriptor, to generate SQL statements that create, read, modify, and delete objects.

The OracleAS TopLink metadata model has three levels of information:

The metadata model describes the simplest case. There are more complex cases in which objects map to partial or multiple rows, and classes map to multiple tables, which are described later in this chapter. For the purposes of introducing mapping, this simple case forms the basis for understanding how mapping works.

OracleAS TopLink interaction with both object models and databases is unintrusive: OracleAS TopLink adapts to the object model and database schema, rather than requiring developers to design their object model or database schema to suit OracleAS TopLink.

OracleAS TopLink Mapping Workbench

The OracleAS TopLink Mapping Workbench is a graphical tool that gives you access to most OracleAS TopLink features. Although the OracleAS TopLink Mapping Workbench does not support the complete OracleAS TopLink feature set, it does support the basic functions required for mapping your application, as well as most of the advanced features.

The graphical nature of the OracleAS TopLink Mapping Workbench makes it easy to create models and mappings. As such, Oracle recommends that you build as much of your project in the OracleAS TopLink Mapping Workbench as possible.

An important feature of the OracleAS TopLink Mapping Workbench is its ability to generate deployment files from your project, either as deployment XML files or Java source code.

For more information about generating deployment files, see "Exporting Project Information" in the Oracle Application Server TopLink Mapping Workbench User's Guide.

Deployment XML Generation

The OracleAS TopLink Mapping Workbench can generate XML files from your project. OracleAS TopLink reads these files at runtime to configure your application. Deployment XML files reduce development time by eliminating the need to regenerate and recompile Java code each time the project changes.

Project Class Generation

The OracleAS TopLink Mapping Workbench can generate Java source files for your project that you compile and run for your application. Often, this generated code deploys faster than XML files, but is less flexible and more difficult to troubleshoot.

OracleAS TopLink Mapping Types

OracleAS TopLink offers several types of mapping, each optimized for different types of information.

Direct Mappings

Direct mappings define how a persistent object refers to objects and attributes that do not have OracleAS TopLink descriptors, such as the JDK classes, primitive types, and other nonpersistent classes. Direct mappings map primitive data types to database data types on a one-to-one basis.

For more information about direct mappings, see "Direct Mappings".

Relationship Mappings

Relationship mappings describe how you manage relationships on the database. OracleAS TopLink uses several different mechanisms to represents relationships in the database, the most common of which is foreign keys. The OracleAS TopLink descriptors include details on the storage and retrieval mechanisms used for the relationship.

For more information about relationship mappings, see "Relationship Mappings".

Inheritance

In object modeling, when one class (the superclass) shares its attributes with another class (the subclass), the subclass is said to inherit those attributes from the superclass or table. Similarly, in the database world, when one table shares information with a subordinate table in the database, the subordinate table inherits information from the main table. Although these two types of inheritance are similar, mapping them properly can be difficult.

OracleAS TopLink supports both object and database inheritance, and enables you to easily map object inheritance to database tables. OracleAS TopLink treats both types of inheritance interchangeably, provided that you map the inheritance in the class descriptors for the superclass and subclass.

For more information about inheritance, see "Inheritance".

Objects and the Database

OracleAS TopLink stores objects in database tables. In most cases, a single row in a database table represents a single object in your OracleAS TopLink application. Several OracleAS TopLink concepts follow from this arrangement, including:

Primary Keys

A primary key is a column or a combination of columns, in a database table that contains a unique identifier for every record in the table. Persistent objects require a primary key. If a table uses a combination of columns to create a unique identifier, this combination of fields is collectively called a composite primary key. In either case, a primary key uniquely identifies each row.

Sequencing

Sequencing is a mechanism to populate the primary key attribute of new objects and entity beans before inserting them into the database.

For more information, see "Sequencing".

Foreign Keys and Object Relationships

Objects stored in one database table (the source objects) can share a relationship with objects in other tables (the target objects). To define these relationships, your tables must include data that identifies which target objects are related to the source object in the relationship.

The target table primary key in the relationship becomes a foreign key in the source table and identifies which objects in the target table are related to the objects in the source table.

For more information, see "Foreign Keys".

Indirection

The standard object reading behavior in Java is that when you read an object, you also read all its related objects, which can be unnecessarily time consuming. The OracleAS TopLink indirection feature enables you to defer reading related objects until they are required. This is also known as lazy reading, lazy loading, and just-in-time reading.

For more information, see "Indirection".

Serialization

In OracleAS TopLink, serialization is the act of writing out (marshalling) an object from its home OracleAS TopLink Java virtual machine (JVM) to another JVM.

For more information, see "Serialization".

General Terms and Concepts

This section outlines some of the more common general concepts you will encounter when dealing with mappings.

Primitive Versus Complex Data

OracleAS TopLink treats certain classes as primitive data types for mapping purposes. These include Strings and Integers. Primitive data types correspond directly to representations in the database fields in which they are stored.

Because of this direct correspondence, there is no need to describe how to map the primitive data. As a result, OracleAS TopLink does not require mapping descriptors for primitive data types.

Object attributes represent complex data. OracleAS TopLink requires class descriptors to define how the attributes and relationships of instances of a particular class are stored and retrieved. Descriptors specify where and how attributes are stored in database tables.

Java Objects

Java objects represent the components or business logic of your application. As the basic building blocks in an OracleAS TopLink application, objects can include data, methods, relationships, and inheritance hierarchies.

Basic Mappings

The OracleAS TopLink Mapping Workbench enables you to set properties and configure the mappings and OracleAS TopLink descriptors for any given project in a graphical environment. To create mappings, use either the OracleAS TopLink Mapping Workbench or the Java code-based API. However, Oracle recommends the OracleAS TopLink Mapping Workbench whenever possible.

Mappings for each class are stored in the class descriptor. OracleAS TopLink uses the descriptor to instantiate objects from the database and to store new or modified objects on the database. The descriptor describes how to store to or retrieve from the database a given class. Object instantiation uses this information to build and store the instantiated objects.

The relationship among the database, the objects and classes, and the descriptor makes up the OracleAS TopLink metadata model.

Figure 3-2 The OracleAS TopLink Metadata Model

Text description of metamap.gif follows.

Text description of the illustration metamap.gif

For more information about the OracleAS TopLink Mapping Workbench, see the Oracle Application Server TopLink Mapping Workbench User's Guide.

This section presents several topics and techniques to optimize your mapping strategy, including:

Direct Mappings

Use direct mapping to map primitive object attributes, or nonpersistent regular objects, such as the JDK classes. For example, use a direct-to-field mapping to store a String attribute in a VARCHAR field.

You can map entity bean attributes using direct mappings without any special considerations.


Note:

When you work with EJBs, do not map the entity context attribute (type javax.ejb.EntityContext).


All direct mappings include optional setGetMethodName() and setSetMethodName() messages. These messages allow OracleAS TopLink to access the attribute through user-defined methods, rather than directly through the attribute.

Direct-to-Field Mappings

The direct-to-field mappings are instances of the DirectToFieldMapping class and require the following elements:

The Descriptor class provides the addDirectMapping() method that creates a new DirectToFieldMapping, sets the attribute and field name parameters, and registers the mapping with the descriptor.

You create a direct-to-field mapping in one of two ways:

Mapping an Attribute

Example 3-1 and Example 3-2 illustrate common ways of mapping one attribute to one field.

Example 3-1 Creating a Direct-to-Field Mapping in Java and Registering It with the Descriptor

// Create a new mapping and register it with the descriptor.
DirectToFieldMapping mapping = new DirectToFieldMapping();
mapping.setAttributeName("city");
mapping.setFieldName("CITY");
descriptor.addMapping(mapping);

Example 3-2 Creating a Mapping that Uses Method Access

This mapping example assumes that the persistent class has getCity() and setCity() methods defined.

// Create a new mapping and register it with the descriptor. 
DirectToFieldMapping mapping = new DirectToFieldMapping();
mapping.setAttributeName("city");
mapping.setFieldName("CITY");
mapping.setGetMethodName("getCity");
mapping.setSetMethodName("setCity");
descriptor.addMapping(mapping);

Example 3-3 Using the Two Overloaded Versions of the Descriptor's addDirectMapping() Method

// Alternate method which does the same thing.
descriptor1.addDirectMapping("city", "CITY");
descriptor2.addDirectMapping("city", "getCity", "setCity", "CITY");
Mapping Multiple Attributes to the Same Field

You must pay special attention when you map more than one attribute to the same field where some mappings are read-only and some are not. By default, with DatabaseLogin.setShouldOptimizeDataConversion(true) -- OracleAS TopLink uses the data type of the attribute of the last writable mapping for all subsequent read-only mappings. In this context, "last" is relative to the order in which the attributes are declared in the mapped class.

This behavior can lead to a loss of precision.

Consider the following hypothetical example: you want to map the class that appears in Example 3-4 to create two different views of the same, underlying database field. You want attribute view1 to represent the database field as an integer and attribute view2 to represent the same database field as a double. Finally, you want attribute view1 to be writable and attribute view2 to be read-only.

Example 3-4 ClassToMap Definition

public class ClassToMap
{
private String name;
private long id;
private int view1; 
private double view2; // READONLY 
... 
}

Furthermore, your database administrator decides that both attributes will be mapped to a single database column, NUM_VIEW of table CLASSTOMAP, declared NUMBER(20,7) -- that is, with a non-zero sub field size which allows storage of both integer values and floating point values with up to 7 digits of precision.

The corresponding project.xml database mapping elements appear in Example 3-5: the first maps attribute view1 to table CLASSTOMAP field NUM_VIEW as writable and the second maps attribute view2 to the same field as read-only.

Example 3-5 project.xml Database Mapping Elements

<database-mapping>
<attribute-name>view1</attribute-name>
<read-only>false</read-only>
<field-name>CLASSTOMAP.NUM_VIEW</field-name>
<type>oracle.toplink.mappings.DirectToFieldMapping</type>
</database-mapping>

<database-mapping>
<attribute-name>view2</attribute-name>
<read-only>true</read-only>
<field-name>CLASSTOMAP.NUM_VIEW</field-name>
<type>oracle.toplink.mappings.DirectToFieldMapping</type>
</database-mapping>

If the database is loaded with a record with CLASSTOMAP.NUM_VIEW value 3.141, you need to use readAllObjects() to get this instance of ClassToMap as shown in Example 3-6.

Example 3-6 Reading Objects of Type ClassToMap

Session sess = SessionManager.getManager().getSession("test"); 
Vector v = sess.readAllObjects(ClassToMap.class) 

In the returned instance, the value of view1 will be 3 and the value of view2 will be 3.0 instead of 3.141. This loss of precision is the result of OracleAS TopLink's method of applying the data type of the last writable mapping which in this example is integer.

In this case, you can choose from either of the following options:

To change your design so that the view1 is read-only and view2 is writable, proceed as follows:

<database-mapping>
<attribute-name>view1</attribute-name>
<read-only>true</read-only>
<field-name>CLASSTOMAP.NUM_VIEW</field-name>
<type>oracle.toplink.mappings.DirectToFieldMapping</type>
</database-mapping>

<database-mapping>
<attribute-name>view2</attribute-name>
<read-only>false</read-only>
<field-name>CLASSTOMAP.NUM_VIEW</field-name>
<type>oracle.toplink.mappings.DirectToFieldMapping</type>
</database-mapping>

For more information about the available methods for DirectToFieldMapping, see the Oracle Application Server TopLink API Reference.

Type Conversion Mappings

Type conversion mappings and instances of the TypeConversionMapping class and require the following elements:

Example 3-7 Creating a Type Conversion Mapping and Registering It with the Descriptor

// Create a new mapping and register it with the descriptor.
TypeConversionMapping typeConversion = new TypeConversionMapping();
typeConversion.setFieldName("J_DAY");
typeConversion.setAttributeName("joiningDate");
typeConversion.setFieldClassification(java.sql.Date.class);
typeConversion.setAttributeClassification(java.util.Date.class);
descriptor.addMapping(typeConversion);

For more information about the available methods for TypeConversionMapping, see the Oracle Application Server TopLink API Reference.

Object Type Mappings

Object type mappings are instances of the ObjectTypeMapping class and require the following elements:

The following methods are useful in a legacy environment or when you want to change the values of the fields:

Example 3-8 Creating an Object Type Mapping and Registering It with the Descriptor

// Create a new mapping and register it with the descriptor.
ObjectTypeMapping typeMapping = new ObjectTypeMapping();
typeMapping.setAttributeName("gender");
typeMapping.setFieldName("GENDER");
typeMapping.addConversionValue("M", "Male");
typeMapping.addConversionValue("F", "Female");
typeMapping.setNullValue("F");
descriptor.addMapping(typeMapping);

For more information about the available methods for ObjectTypeMapping, see the Oracle Application Server TopLink API Reference.

Relationship Mappings

Relationship mappings define how persistent objects reference other persistent objects. OracleAS TopLink supports several relationship mapping types, as described in this section.

Relationships and Entity Beans

Persistent objects use relationship mappings to store references to instances of other persistent classes. The appropriate mapping type is selected based on the cardinality of the relationship (for example: a one-to-one or one-to-many). Entity beans can have relationships to regular Java objects, other entity beans, or both.

Mappings Between Entity Beans

A bean that has a relationship to another bean acts as a client of that bean--it does not access the actual bean directly but acts through the remote (EJB 1.1) or local (EJB 2.0) interface of the bean. For example, if an OrderBean is related to a CustomerBean, it has an instance variable of type Customer (the Local or Remote interface of the CustomerBean) and accesses only those methods defined on the Customer interface.


Note:

Although beans must refer to each other through their remote (EJB 1.1) or local (EJB 2.0) interface, all OracleAS TopLink descriptors and projects refer to the bean class. For example, if you map beans and define relationships between them, you need load only the bean classes into the OracleAS TopLink Mapping Workbench--not the Remote, Local, or Home interfaces. When you define a relationship mapping in both the OracleAS TopLink Mapping Workbench and code API, the reference class is always the bean class.


Most OracleAS TopLink relationship mapping functionality is available regardless of the EJB specification supported by your J2EE container or application server. However, there are some differences between OracleAS TopLink support for EJB 1.1 and EJB 2.0.

Relationship Mappings Under EJB 1.1

The EJB 1.1 specification does not specify how entity beans store an object reference to another entity bean; as a result, if you are using an EJB 1.1-compliant container, this normally prevents you from mapping relationships between entity beans. However, OracleAS TopLink includes support for relationships that exceeds what is available in the EJB 1.1 specification, and allows the creation of inter-bean relationships.

Relationship Mappings Under EJB 2.0

The EJB 2.0 specification defines methods for relating beans to one another. OracleAS TopLink support for the EJB 2.0 specification includes the following concepts:

The EJB 2.0 specification also imposes many restrictions on CMP relationships, some of which are not enforced by OracleAS TopLink. Therefore, although OracleAS TopLink offers more flexibility in developing applications, if the application must be fully EJB 2.0-compliant, be careful about which features you include in your application.

Some of the EJB 2.0 restrictions that OracleAS TopLink does not enforce include:

The EJB 2.0 specification describes additional restrictions to the mapping and runtime behavior of EJB 2.0 CMP beans.

For more information about the Enterprise JavaBeans and the EJB 2.0 specification, see

http://java.sun.com/products/ejb/
http://java.sun.com/products/ejb/docs.html
http://java.sun.com/j2ee/white/index.html

In addition, although EJB 2.0 support for indirection is limited, OracleAS TopLink does enable you to implement OracleAS TopLink valueholder indirection for one-to-one relationships, and transparent indirection for one-to-many and many-to-many relationships.

For more information, see "Indirection".

Importing EJB 2.0 Relationship Metadata in the OracleAS TopLink Mapping Workbench

The OracleAS TopLink Mapping Workbench can obtain relationship metadata from the ejb-jar.xml file.

For more information on how to update OracleAS TopLink relationships in the OracleAS TopLink Mapping Workbench from the ejb-jar.xml deployment descriptor, see "Working with project properties" in the Oracle Application Server TopLink Mapping Workbench User's Guide.

Mappings Between Entity Beans and Java Objects

Entity beans represent independent business objects. Objects that depend on the entity bean are often implemented as Java classes, and included as part of the entity bean on which they depend. The following relationship mappings may exist between an entity bean and regular Java objects:

One-to-One Mappings

One-to-one mappings represent simple pointer references between two objects. One-to-one mappings for relationships between entity beans, or between an entity bean and a regular Java object, where the entity bean is the source and the regular Java object is the target of the relationship.

One-to-one mappings are instances of the OneToOneMapping() class and require the following elements:

Bidirectional Relationships

If the mapping has a bidirectional relationship in which the two classes in the relationship reference each other with one-to-one mappings, then set up the foreign key information as follows:

It is also possible to set up composite foreign key information by sending the addForeignKeyFieldName() and addTargetForeignKeyFieldName() messages. Because OracleAS TopLink enables indirection by default, the attribute must be a ValueHolderInterface.


Caution:

When your application does not use a cache, enable indirection for at least one object in a bidirectional relationship. In rare cases, disabling indirection on both objects in the bidirectional relationship can lead to infinite loops.


Example 3-9 Creating a Simple One-to-One Mapping and Registering It with the Descriptor

// Create a new mapping and register it with the descriptor.
OneToOneMapping oneToOneMapping = new OneToOneMapping();
oneToOneMapping.setAttributeName("address");
oneToOneMapping.setReferenceClass(Address.class);
oneToOneMapping.setForeignKeyFieldName("ADDRESS_ID");
descriptor.addMapping(oneToOneMapping);

Example 3-10 Implementing a Bidirectional Mapping Between Two Classes that Reference Each Other

The foreign key is stored in the Policy's table referencing the composite primary key of the Carrier.

// In the Policy class, which will hold the foreign key, create the mapping that 
references the Carrier class.
OneToOneMapping carrierMapping = new OneToOneMapping();
carrierMapping.setAttributeName("carrier");
carrierMapping.setReferenceClass(Carrier.class);
carrierMapping.addForeignKeyFieldName("INSURED_ID", "CARRIER_ID");
carrierMapping.addForeignKeyFieldName("INSURED_TYPE", "TYPE");
descriptor.addMapping(carrierMapping);. . .
// In the Carrier class, create the mapping that references the Policy class.
OneToOneMapping policyMapping = new OneToOneMapping();
policyMapping.setAttributeName("masterPolicy");
policyMapping.setReferenceClass(Policy.class);
policyMapping.addTargetForeignKeyFieldName("INSURED_ID", "CARRIER_ID");
policyMapping.addTargetForeignKeyFieldName("INSURED_TYPE", "TYPE");
descriptor.addMapping(policyMapping);

For more information about the available methods for OneToOneMapping, see the Oracle Application Server TopLink API Reference.

For more information about one-to-one mappings, see the Oracle Application Server TopLink Mapping Workbench User's Guide.

One-to-One Mappings and EJBs

To maintain EJB compliance, the object attribute that points to the target of the relationship must be the remote (EJB 1.1) or local (EJB 2.0) interface type--not the bean class.

OracleAS TopLink provides variations on one-to-one mappings that allow you to define complex relationships when the target of the relationship is a dependent Java object. For example, variable one-to-one mappings enable you to specify variable target objects in the relationship. These variations are not available for entity beans, but are valid for dependent Java objects.

For more information, see the "Variable One-to-One Mappings".

Aggregate Object Mappings

Two objects are related by aggregation if there is a strict one-to-one relationship between the objects, and if all the attributes of the second object can be retrieved from the same table(s) as the owning object. So if the target (or child) object exists, then the source (or parent) object must also exist. The child object cannot exist without its parent.

Aggregate object mappings are instances of the AggregateObjectMapping class. This mapping relates to an attribute in each of the parent classes. Aggregate object mappings require the following information:

Aggregate object mappings also require the following modifications to the target class descriptor:

By default, the mapping allows null references to its target class, so it does not create an instance of the target object. To prevent a parent from having a null reference, send the dontAllowNull( ) message, which results in an instance of the child with its attributes set to null.

Example 3-11 Creating an Aggregate Object Mapping for the Employee Source Class and Registering It with the Descriptor

// Create a new mapping and register it with the source descriptor.
AggregateObjectMapping aggregateMapping = new AggregateObjectMapping();
aggregateMapping.setAttributeName("employPeriod");
aggregateMapping.setReferenceClass(Period.class);
descriptor.addMapping(aggregateMapping);

Example 3-12 Creating the Descriptor of the Period Aggregate Target Class

The aggregate target descriptor does not need a mapping to its parent, nor does it need any table or primary key information.

// Create a descriptor for the aggregate class. The table name and primary key 
are not specified in the aggregate descriptor.
Descriptor descriptor = new Descriptor();
descriptor.setJavaClass(Period.class);
descriptor.descriptorIsAggregate();

// Define the attribute mappings or relationship mappings.
descriptor.addDirectMapping("startDate", "START_DATE");
descriptor.addDirectMapping("endDate", "END_DATE");
return descriptor;

Example 3-13 Creating an Aggregate Object Mapping for the Project, Which is Another Source Class that Contains a Period

The field names must be translated in the Project descriptor. No changes need to be made to the Period class descriptor to implement this second parent.

// Create a new mapping and register it with the parent descriptor.
AggregateObjectMapping aggregateMapping = new AggregateObjectMapping();
aggregateMapping.setAttributeName("projectPeriod");
aggregateMapping.setReferenceClass(Period.class);
aggregateMapping.addFieldNameTranslation("S_DATE", "START_DATE");
aggregateMapping.addFieldNameTranslation("E_DATE", "END_DATE");
descriptor.addMapping(aggregateMapping);

For more information about the available methods for AggregateObjectMapping, see the Oracle Application Server TopLink API Reference.

Aggregate Object Mappings and EJBs

You can use aggregate mappings with entity beans when the source of the mapping is an entity bean and the target is a regular Java object. An entity bean cannot be the target of an aggregate object mapping.


Note:

Aggregate objects are privately owned and must not be shared or referenced by other objects.


For more information about aggregate object mappings, see the Oracle Application Server TopLink Mapping Workbench User's Guide.

One-to-Many Mappings

One-to-many mappings represent the relationship between a single source object and a collection of target objects.

For more information about one-to-many mappings, see the Oracle Application Server TopLink Mapping Workbench User's Guide.

One-to-many mappings are instances of the OneToManyMapping class and require the following elements:

Example 3-14 Creating a Simple One-to-Many Mapping and Registering It with the Descriptor

// In the Employee class, create the mapping that references the Phone class.
oneToManyMapping = new OneToManyMapping();
oneToManyMapping.setAttributeName("phoneNumbers");
oneToManyMapping.setReferenceClass(PhoneNumber.class);
oneToManyMapping.setTargetForeignKeyFieldName("EMPID");
descriptor.addMapping(oneToManyMapping);
. . .
// In the Phone class, which will hold the foreign key, create the mapping that 
references the Employee class.
OneToOneMapping oneToOneMapping = new OneToOneMapping();
oneToOneMapping.setAttributeName("owner");
oneToOneMapping.setReferenceClass(Employee.class);
oneToOneMapping.setForeignKeyFieldName("EMPID");
descriptor.addMapping(oneToOneMapping);

In addition to the API Example 3-14 illustrates, other common API for use to implement indirection in aggregate collection include:

For more information about the available methods for OneToManyMapping, see the Oracle Application Server TopLink API Reference.

One-to-Many Mappings and EJBs

Use one-to-many mappings for relationships between entity beans or between an entity bean and a collection of privately owned regular Java objects. When you create one-to-many mappings, also create a one-to-one mapping from the target objects back to the source. The object attribute that contains a pointer to the bean must be the remote (EJB 1.1) or local (EJB 2.0) interface type--not the bean class.

OracleAS TopLink automatically maintains back-pointers when you create or update bidirectional relationships between beans.

For more information, see "Maintaining Bidirectional Relationships".

Aggregate Collections

Aggregate collection mappings represent the aggregate relationship between a single-source object and a collection of target objects. Unlike the OracleAS TopLink one-to-many mappings, there is no back reference required for the aggregate collection mappings because the foreign key relationship is resolved by the aggregation.

Aggregate collection mappings require a target table for the target objects.

To implement an aggregate collection mapping:

When to Use Aggregate Collections

Although similar in behavior to one-to-many mappings, an aggregate collection is not a replacement for one-to-many mappings. Use aggregate collections when the target collections are reasonable in size and a one-to-one mapping from the target to the source proves difficult.

Because one-to-many relationships offer better performance and are more robust and scalable, consider using a one-to-many relationship rather than an aggregate collection. In addition, aggregate collections are privately owned by the source of the relationship and must not be shared or referenced by other objects.

Aggregate Collections and Inheritance

Aggregate collection descriptors can make use of inheritance, but you must declare the subclasses as aggregate collections as well. The subclasses can have their own mapped tables, or share the table with their parent class.

In a Java vector, the owner references its parts; in a relational database, the parts reference their owners. Relational databases use this implementation to make querying more efficient.

Java Implementation

Aggregate collection mappings are instances of the AggregateCollectionMapping class and require the following elements:

Example 3-15 Creating a Simple Aggregate Collection Mapping and Registering It with the Descriptor

// In the PolicyHolder class, create the mapping that references the Phone class
AggregateCollectionMapping phonesMapping = new AggregateCollectionMapping();
phonesMapping.setAttributeName("phones");
phonesMapping.setGetMethodName("getPhones");
phonesMapping.setSetMethodName("setPhones");
phonesMapping.setReferenceClass("Phone.class");
phonesMapping.dontUseIndirection();
phonesMapping.privateOwnedRelationship;
phonesMapping.addTargetForeignKeyFieldName("INS_PHONE.HOLDER_SSN","HOLDER.SSN");
descriptor.addMapping(phonesMapping);

In addition to the API Example 3-15 illustrates, other common API for use to implement indirection in aggregate collection mappings include:

For more information about the available methods for AggregateCollectionMapping, see the Oracle Application Server TopLink API Reference.

Aggregate Collection Mappings and EJBs

You can use aggregate collection mappings with entity beans if the source of the relationship is an entity bean or Java object, and the mapping targets are regular Java objects. Entity beans cannot be the target of an aggregate object mapping.

Direct Collection Mappings

Direct collection mappings store collections of Java objects that are not OracleAS TopLink-enabled. Direct collections usually store Java types, such as strings.

Direct collection mappings are instances of the DirectCollectionMapping class and require the following elements:

Example 3-16 Creating a Simple Direct Collection Mapping

DirectCollectionMapping directCollectionMapping = new DirectCollectionMapping();
directCollectionMapping.setAttributeName ("responsibilitiesList");
directCollectionMapping.setReferenceTableName ("RESPONS");
directCollectionMapping.setDirectFieldName("DESCRIP");
directCollectionMapping.setReferenceKeyFieldName ("EMP_ID");
directCollectionMapping.useCollectionClass (Vector.class); // the default
descriptor.addMapping(directCollectionMapping);

In addition to the API Example 3-16 illustrates, other common API for use with direct collection mappings include:

For more information about the available methods for DirectCollectionMapping, see the Oracle Application Server TopLink API Reference.

Many-to-Many Mappings

Many-to-many mappings represent the relationships between a collection of source objects and a collection of target objects. This requires an intermediate table that manages the associations between the source and target records.

Many-to-many mappings are instances of the ManyToManyMapping class and requires the following elements:

Example 3-17 Code that Creates a Simple Many-to-Many Mapping

// In the Employee class, create the mapping that references the Project class.
ManyToManyMapping manyToManyMapping = new ManyToManyMapping();
manyToManyMapping.setAttributeName("projects");
manyToManyMapping.setReferenceClass(Project.class);
manyToManyMapping.setRelationTableName("PROJ_EMP");
manyToManyMapping.setSourceRelationKeyFieldName ("EMPID");
manyToManyMapping.setTargetRelationKeyFieldName ("PROJID");
descriptor.addMapping(manyToManyMapping);

In addition to the API Example 3-17 illustrates, other common API for use with many-to-many mappings include:

For more information about the available methods for ManyToManyMapping, see the Oracle Application Server TopLink API Reference.

Many-to-Many Mappings and EJBs

When you use CMP, many-to-many mappings are valid only between entity beans, and cannot be privately owned. The only exception is when a many-to-many mapping is used to implement a logical one-to-many mapping with a relation table.

OracleAS TopLink automatically maintains back-pointers when you create or update bidirectional relationships.

For more information, see "Maintaining Bidirectional Relationships".

For more information about ManyToManyMapping, see the Oracle Application Server TopLink Mapping Workbench User's Guide.

Indirection

By default, when an OracleAS TopLink application reads an object, it also reads all its related objects. For example, given an object, CAR, with related objects, TIRES and RADIO, reading the CAR object forces reading of the TIRES and RADIO objects at the same time. This method is inefficient if the reason for reading in the CAR object has nothing to do with the related objects (for example: when you read CAR to check one of its attributes, such as COLOR).

OracleAS TopLink indirection gives you the ability to replace the related objects (TIRES and RADIO, in this example) with an indirection object. An indirection object is a placeholder that represents related objects, but prevents them from being read until they are actually required. If you never need the related objects, they are never read from the database.

OracleAS TopLink supports three main types of indirection:

Indirection represents an effective way to improve the efficiency of your application and we recommend it be implemented wherever it is supported by your application and its usage patterns.

For more information about implementing indirection in code, see "Implementing Indirection in Java".

Valueholder Indirection

Valueholder indirection is a native OracleAS TopLink feature that implements the OracleAS TopLink ValueHolderInterface on your objects to achieve indirection. A valueholder represents an instance of a related class and stores the information necessary to retrieve the object it represents from the database. If the application does not access the valueholder, the replaced object is never read from the database.

If you use method access, the get and set methods specified for the mapping must access an instance of ValueHolderInterface, rather than the object that the valueholder is referencing. To obtain the object represented by the valueholder, use the getValue() and setValue() methods of the ValueHolderInterface class. You can hide the getValue and setValue methods of the ValueHolderInterface inside get and set methods.

You can change the attribute types in the class editor, but if you do, also change the attribute types in your Java code, as well as their accessor methods.

If the instance variable returns a vector instead of an object, define the valueholder in the constructor as follows:

addresses = new ValueHolder(new Vector());

The application uses the getAddress() and setAddress() methods to access the Address object. When you use indirection, OracleAS TopLink uses the getAddressHolder() and setAddressHolder() methods to save instances to and retrieve instances from the database.

Example 3-18 Implementing the Employee Class Using Indirection with Method Access for a One-to-One Mapping to Address

This example modifies the class definition so that the address attribute of Employee is a ValueHolderInterface, rather than an Address, and supplies the appropriate get and set methods.

// Initialize ValueHolders in Employee Constructor
public Employee() {
address = new ValueHolder();
}
protected ValueHolderInterface address;

// 'Get' and `Set' accessor methods registered with the mapping and used by 
OracleAS TopLink.
public ValueHolderInterface getAddressHolder() {
return address;
}
public void setAddressHolder(ValueHolderInterface holder) {
address = holder;
}

// `Get' and `Set' accessor methods used by the application to access the 
attribute.
public Address getAddress() {
return (Address) address.getValue();
}
public void setAddress(Address theAddress) {
address.setValue(theAddress);
}

Proxy Indirection

Proxy indirection enables you to use dynamic proxy objects as stand-ins for a defined interface. You can configure all the following mapping types to use proxy indirection, which gives you the benefits of indirection without the need to include OracleAS TopLink classes in your domain model:

Note that all these mapping types map one-to-one relationships.

The useProxyIndirection()method indicates that OracleAS TopLink must use proxy indirection for the current mapping. When you read the source object from the database, OracleAS TopLink creates a proxy for the target object and uses it in place of the target object. When you call any method other than toString() on the proxy, OracleAS TopLink reads the target object from the database.

Proxy indirection is not directly supported in the OracleAS TopLink Mapping Workbench. To implement proxy indirection, use the useProxyIndirection method in an amendment method.

Proxy indirection does not use the OracleAS TopLink ValueholderInterface, and nor are target objects typed as ValueHolderInterface. Instead, to implement proxy indirection, make changes to both the object model and the descriptor mapping for the source object.

To use proxy indirection, your domain model must satisfy the following criteria:

In the descriptor, invoke the useProxyIndirection method in the source object descriptor that defines mapping between the source and target objects.

Example 3-19 Implementing Proxy Indirection on the Source Descriptor

The Employee class has an attribute, ADDRESS, of type Address. The Address attribute is mapped using a one-to-one mapping from Employee (source) to Address (target) and uses proxy indirection. The code includes the steps for building this relationship.

//Step 1. Define an interface "IAddress" for "Address"
public interface IAddress {
    public String getCity();
    public void setCity(String aCity);
}

// Step 2. Implement this interface on the "Address" class
public class Address implements IAddress {
    String city;
    public String getCity() { return city;}
    public void setCity(String aCity){city = aCity;}

    public Address() {
    ...
}

}

//Step 3. Declare the attribute "address" as interface "IAddress" on the 
Employee.

public class Employee {
    public BigInteger id;
    public String firstName;
    public String lastName; 
    public IAddress address;

//4. Configure the Set and get methods "getAddress()", "setAddress()" to use 
interface IAddress"

    // get and set methods for instance variables
    public IAddress getAddress() {return this.address;}
    public void setAddress (IAddress newAddress) 
    {this.address=newAddress;}


    public Employee() {
    ...
    }
}

//5. The mapping between Employee and Address must invoke the 
useProxyIndirection() api. Also, the target class, which implements the 
interface must be passed to the setReferenceClass() method as the argument.

//Define the 1:1 mapping, and specify that ProxyIndirection should be used
OnetoOnemapping addressMapping = new OneToOneMapping();
addressMapping.setAttributeName("address");
addressMapping.setReferenceClass(Address.class);
addressMapping.setForeignKeyFieldName("ADDRESS_ID");
addressMapping.setSetMethodName("setAddress");
addressMapping.setGetMethodName("getAddress");
addressMapping.useProxyIndirection();
descriptor.addMapping(addressMapping);

Proxy Indirection Restrictions

You cannot register the target of a proxy indirection implementation with a Unit of Work. Instead, first register the source object with the Unit of Work. This enables you to retrieve a target object clone with a get...() call against the source objects clone.

For example:

UnitOfWork uow = session.acquireUnitOfWork();
Employee emp = (Employee)session.readObject(Employee.class);

// Register the source object
Employee empClone = (Employee)uow.registerObject(emp); 

// All of source object's relationships are cloned when source object is cloned
Address addressClone = empClone.getAddress();
addressClone.setCity("Toronto"); 

For more information about clones and the Unit of Work, see "Understanding the Unit of Work".

Transparent Indirection

Transparent indirection enables you to declare any relationship attribute of a persistent class that holds a collection of related objects as a java.util.Collection, java.util.Map, java.util.Vector, or java.util.Hashtable. OracleAS TopLink uses an indirection object that implements the appropriate interface, and performs just-in-time reading of the related objects.

When using transparent indirection, you do not have to declare the attributes as ValueHolderInterface.

You can specify transparent indirection from the OracleAS TopLink Mapping Workbench. Newly created collection mappings use transparent indirection by default if their attribute is not a ValueHolderInterface.

Do not include OracleAS TopLink classes in the domain class for transparent indirection.

Choosing Your Indirection Type

Although there are no universal rules for the use of indirection, the following guidelines illustrate when indirection is beneficial, and helps you choose the appropriate type of indirection.

Choosing No Indirection

Because it delays database reads until they are required, indirection produces an increase in performance. However, if you have a relationship between objects that are always called together, the benefit does not apply. For example, if you have a pair of objects that are always called together to populate a Web page, there is no benefit to delay the reading of the target of the relationship, because it will be called at the same time as the source object every time. If you have objects that you always call together, do not implement indirection.

Choosing Valueholder Indirection

Use valueholder indirection if at least one of the following conditions exists:

Choosing Proxy Indirection

Use proxy indirection if you are not applying indirection with EJB entity beans as targets.

Choosing Transparent Indirection

When you create one-to-many or many-to-many relationships in the OracleAS TopLink Mapping Workbench, OracleAS TopLink automatically implements transparent indirection. This provides the best possible performance for large relationship graphs and must not be disabled.

Indirection and EJBs

OracleAS TopLink offers mechanisms to implement indirection for relationships between EJBs. As with regular Java objects, these mechanisms include:

The Oracle Application Server TopLink Mapping Workbench User's Guide describes these indirection mechanisms.

Consider the following guidelines when you use indirection with EJBs, particularly when you migrate objects between client and server:

EJB 2.0 and Indirection

When both the source and target are entity beans, the indirection policies for container-managed relationship fields under the EJB 2.0 specification must be one of the following:

Because subclasses are code-generated, all indirection is hidden from the user.

Serialization

OracleAS TopLink supports Java serialization, which enables you to write objects out to one JVM and read objects back from the other JVM. Preparing the objects for transport is known as marshalling; receiving objects back is known as unmarshalling. In an OracleAS TopLink application, serialization occurs between a JVM with OracleAS TopLink and a non-OracleAS TopLink JVM. If you serialize to another JVM with OracleAS TopLink, consider using a remote session instead.

For more information, see "Remote Session".

Serialization and Indirection

A common cause of problems with serialization is the use of indirection in serialized objects. Indirection valueholders rely on the OracleAS TopLink session for context (mapping information, JDBC connectivity, and so on); however, because the OracleAS TopLink session is stored in the object in a transient variable, it does not survive serialization, leaving the serialized valueholders with no context and no way to resolve the links to the data they represent. As a result, when you marshall the object for serialization, the values held by valueholders are replaced with a null value. If the application on the receiving JVM invokes the valueholder, the result is a null pointer exception.

Note that no null pointer exception is thrown during the serialization process, nor does OracleAS TopLink prevent you from serializing an untriggered valueholder. This enables you to serialize objects and retain the efficiency advantages of indirection if you know that the receiving JVM does not use the valueholders.

Triggering Valueholders During Marshalling

A common way to avoid null pointer exceptions in the receiving JVM is to selectively trigger valueholders before serializing them.

Java serialization supports a callback mechanism that enables you to execute a special type of method on an object before serializing it. Specifically, if a writeObject method exists on the object Java serialization executes the method.

For example:

protected void writeObject(ObjectOutputStream out) throws IOException

This mechanism presents an opportunity to selectively trigger valueholders. You can:

Helper classes are the most flexible way to trigger valueholders, because you can use a single helper class for several objects. When deciding how to trigger valueholders, we recommend the following methods:

Merging Clones on Deserialization

Unmarshalling a serialized object always occurs in the context of a Unit of Work when you integrate changes made outside of the JVM with the affected objects in the OracleAS TopLink application. Several options are available for the merge:

Limitations on Merge

To maintain data integrity, OracleAS TopLink imposes a restriction on merging back serialized objects. If the outside JVM adds objects to the structure passed to it, and then passes back the new objects, OracleAS TopLink merges those objects into the model if one of the following conditions is met:

Primary Keys

A primary key is a column (or combination of columns) that contains a unique identifier for every record in the table. OracleAS TopLink requires that every table that stores persistent objects has a primary key. The following concepts and techniques apply to primary keys:

Under most circumstances, you set primary key information in the OracleAS TopLink Mapping Workbench for persistent Java objects and EJB entity beans. Alternatively, set the primary key manually in Java code.

For more information, see "Implementing Primary Keys in Java".

Primary Keys and EJB Entity Beans

A primary key is a mechanism by which OracleAS TopLink and other applications identify persistent objects and entity beans. EJB entity beans use primary keys in much the same way as regular Java objects, and as with Java objects, you usually set primary keys for entity beans in the OracleAS TopLink Mapping Workbench.

EJB entity beans support both simple primary keys, which are composed of information from a single field in the bean, and composite primary keys, which are composed of information from one or more fields and are stored in a custom class.

Sequencing

When you create tables that do not include a unique key suitable for use as a primary key, use sequencing to assign an identifier to each record. In most cases, you configure sequencing through the OracleAS TopLink Mapping Workbench.

For more information, see "Working with Sequencing" in the Oracle Application Server TopLink Mapping Workbench User's Guide. For more information about implementing sequencing in Java code, see "Implementing Sequence Numbers in Java".

This section describes how to assign primary keys to objects that use sequencing, and includes discussions on:

Sequencing and Database Tables

OracleAS TopLink offers three ways to implement sequencing. Although each method is unique, the three techniques have some commonality.

You store persistent objects for your application in database tables that represent the class of instantiated object. Each row of the table represents an instantiated object from that class, and one column in that table holds the primary key for each object. Sequencing populates the primary key row in the table.

When you configure sequencing, specify two settings for these tables, regardless of the type of sequencing you plan to use:

In addition to these two elements, table sequencing requires you to specify a SEQ_NAME (a name to identify the class in a special sequencing table name) for each sequenced object. You configure these elements in the OracleAS TopLink Mapping Workbench.

Sequencing and Preallocation Size

To improve sequencing efficiency, OracleAS TopLink allows you to preallocate sequence numbers. Preallocation enables OracleAS TopLink to build a pool of available sequence numbers that are assigned to new objects as they are created and inserted into the database. OracleAS TopLink assigns numbers from the pool until the pool is exhausted.

The preallocation size specifies the size of the pool of available numbers. Preallocation improves sequencing efficiency by substantially reducing the number of database accesses required by sequencing. By default, OracleAS TopLink sets preallocation size to 50. You can specify preallocation size either in the OracleAS TopLink Mapping Workbench or as part of the session login.

For more information about setting sequencing parameters at session login, see "Setting Sequencing at Login".

Preallocation is available in table sequencing and is required for Oracle native sequencing.

Table Sequencing

Table sequencing involves creating and maintaining an extra database table that includes sequencing information for sequenced objects in the project. OracleAS TopLink maintains this table to track sequence numbers.

Sequencing information appears in this table for any class that uses sequencing. The default table is called SEQUENCE and contains two columns:

The rows of the SEQUENCE table represent every class that participates in sequencing. When you configure sequencing in the OracleAS TopLink Mapping Workbench, you specify the SEQ_NAME for the class. OracleAS TopLink adds a row with that name to the SEQUENCE table and initializes the SEQ_COUNT column to the value 1.

You can create the SEQUENCE table on the database in one of two ways:

Using the SEQ_COUNT Column

OracleAS TopLink includes an internal mechanism that manages table sequencing. This mechanism maintains a pool (a vector or array) of preallocated values for each sequenced class. When OracleAS TopLink exhausts this pool of values, it acquires a new pool of values, as follows:

  1. OracleAS TopLink accesses the database, requesting that the SEQ_COUNT for the given class (identified by the SEQ_NAME) be incremented by the preallocation size and the result returned.

    For example, consider the SEQUENCE table in Figure 3-4. If you create a new purchase order and OracleAS TopLink has exhausted its pool of sequence numbers, then OracleAS TopLink executes SQL to increment SEQ_COUNT for SEQ_PURCH_ORDER by the preallocation size (in this case, the OracleAS TopLink default of 50). The database increments SEQ_COUNT for SEQ_PURCH_ORDER to 1600 and returns this number to OracleAS TopLink.

  2. OracleAS TopLink calculates a maximum and minimum value for the new sequence number pool and creates the vector of values.

  3. OracleAS TopLink populates the object sequence attribute with the first number in the array and writes the object to the class table.

As you add new objects to the class table, OracleAS TopLink continues to assign values from the pool until it exhausts the pool. When the pool is exhausted, OracleAS TopLink again requests new values from the table.

Default Versus Custom Tables

In most cases, you implement table sequencing using the default table parameters. However, you may want to leverage the Custom Table option if:

Oracle Native Sequencing

OracleAS TopLink support for native sequencing with Oracle databases is similar to table sequencing, except that OracleAS TopLink does not maintain a table in the database. Instead, the Oracle database contains a SEQUENCE object that stores the current maximum number and preallocation size for sequenced objects.

Understanding the Oracle SEQUENCE Object

The Oracle SEQUENCE object implements a strategy that closely resembles OracleAS TopLink sequencing: it implements an INCREMENT construct that parallels the OracleAS TopLink preallocation size, and a sequence.nextval construct that parallels the SEQ_COUNT field in the OracleAS TopLink SEQUENCE table in table sequencing. This implementation enables OracleAS TopLink to use the Oracle SEQUENCE object as if it were an OracleAS TopLink SEQUENCE table, but eliminates the need for OracleAS TopLink to create and maintain the table.

As with table sequencing, OracleAS TopLink creates a pool of available numbers by requesting that the Oracle SEQUENCE object increment the sequence.nextval and return the result. Oracle adds the value, INCREMENT, to the sequence.nextval, and OracleAS TopLink uses the result to build the sequencing pool.

The key difference between this process and the process involved in table sequencing is that OracleAS TopLink is unaware of the INCREMENT construct on the SEQUENCE object. OracleAS TopLink sequencing and the Oracle SEQUENCE object operate in isolation. To avoid sequencing errors in the application, set the OracleAS TopLink preallocation size and the Oracle SEQUENCE object INCREMENT to the same value.

Using SEQUENCE Objects

Your Database Administrator (DBA) must create a SEQUENCE object on the database for every sequencing series your application requires. If every class in your application requires its own sequence, the DBA creates a SEQUENCE object for every class; if you design several classes to share a sequence, the DBA need only create one SEQUENCE object for those classes.

For example, consider the case of a sporting goods manufacturer that manufactures three styles of tennis racquet. The data for these styles of racquet are stored in the database as follows:

The manufacturer can:

Native Sequencing with Other Databases

Several databases support a type of native sequencing in which the database management system (DBMS) generates the sequence numbers. When you create a class table for a class that uses sequencing, include a specified primary key column, and set the column type as follows:

When you insert a new object into the table, OracleAS TopLink populates the object before insertion into the table, but does not include the sequence number. As the database inserts the object into its table, the database automatically populates the primary key field, with a value equal to the primary key of the previous object, plus 1.

At this point, and before the transaction closes, OracleAS TopLink reads back the primary key for the new object so that the object has an identity in the OracleAS TopLink cache.

Sequencing with CMP Entity Beans

To implement sequencing for CMP entity beans, use a sequencing strategy that implements preallocation, such as table sequencing or Oracle native sequencing. Preallocation ensures that the bean primary key is available at ejbPostCreate() time. If use native sequencing as offered in Sybase, Microsoft SQL Server, or Informix databases, be aware that:

OracleAS TopLink CMP Integration with IBM WebSphere

The OracleAS TopLink CMP integration with IBM WebSphere does not automatically provide the primary key after calling the ejbCreate method. If you deploy to a WebSphere server, explicitly set the primary key in the ejbCreate method. Example 3-20 illustrates this call in a WebSphere integration.

Example 3-20 Setting Primary Key in IBM WebSphere

public Integer ejbCreate() throws CreateException {
    oracle.toplink.ejb.cmp.was.SessionLookupHelper.getHelper().getSession(this)
      .getActiveUnitofWork().assignSequenceNumber(this);
    return null
}

OracleAS TopLink CMP Integration with BEA WebLogic

In the OracleAS TopLink CMP integration with BEA WebLogic, OracleAS TopLink automatically sets the primary key field on the bean. You do not pass the key value as a parameter to the create() method, nor set them in the create() method.

Example 3-21 Setting Primary Key in BEA WebLogic

public Integer ejbCreate() throws CreateException {
    return null;
}

The additional line of code looks up the correct session and uses it to assign a sequence number to the bean.

For more information about how to configure sequencing, see the Oracle Application Server TopLink Mapping Workbench User's Guide.

Sequencing with Stored Procedures

If you have stored procedures that perform sequencing for your application, use an amendment method to direct sequencing queries to use the stored procedures.

Example 3-22 Calling a Stored Procedure for Sequencing

DataModifyQuery seqUpdateQuery = new DataModifyQuery();
StoredProcedureCall call = new StoredProcedureCall();
call.setProcedureName("UPDATE_SEQ");
seqUpdateQuery.addArgument("SEQ_NAME"};
seqUpdateQuery.setCall(call)
project.getLogin().setUpdateSequenceQuery(seqUpdateQuery));

Example 3-22 illustrates specifying a stored procedure for sequence updates. The name of the stored procedure must match the name specified in the setProcedureName call (in this case, UPDATE_SEQ). The seqUpdateQuery.addArgument contains one argument, the sequence name.

Example 3-23 illustrates the use of a stored procedure for sequence selects.

Example 3-23 Using a Stored Procedure for Sequence Selects

ValueReadQuery seqReadQuery = new ValueReadQuery();
StoredProcedureCall call = new StoredProcedureCall();
call.setProcedureName("SELECT_SEQ");
seqReadQuery.addArgument("SEQ_NAME"};
seqReadQuery.setCall(call)
project.getLogin().setSelectSequenceNumberQuery (seqReadQuery));

The name of the stored procedure must match the name specified in the setProcedureName call (in this case, SELECT_SEQ). The seqUpdateQuery.addArgument contains one argument, the sequence name.

Foreign Keys

A foreign key is a combination of columns that reference a unique key, usually the primary key, in another table. As with a primary key, a foreign key can be any number of fields, all of which are treated as a unit. A foreign key and the parent key it references must have the same number and types of fields.

OracleAS TopLink enables you to specify two types of foreign keys:

Relationship mappings use foreign keys to search the database for the information it requires to instantiate the target object or objects. For example, if every Employee has an attribute, address, that contains an instance of Address (which has its own descriptor and table), then the one-to-one mapping for the address attribute specifies foreign key information to find an address for a particular Employee.

Multiple Table Mappings

OracleAS TopLink enables you to store the information for a single class in multiple tables. This feature offers you the flexibility to create the objects for your application without imposing any new design requirements on your database schema.

For example, you can create a class called EMPLOYEE that contains not just personal information about the employees, but also business information, such as salary. If your database schema stores salaries in a separate table from basic employee information, OracleAS TopLink multiple table mappings support enables you to create the class you require. Use multiple tables when either of the following is true:

You can associate information for the class using primary keys or foreign keys.

For more information about mapping a class to multiple tables, see "Working with Multiple Tables" in the Oracle Application Server TopLink Mapping Workbench User's Guide.

For more information about implementing multiple table mappings in code, see "Implementing Multiple Tables in Java".

Mapping and Enterprise JavaBeans

To enable container-managed persistent (CMP) storage of entity beans in an Enterprise JavaBean (EJB) application, map the attributes on the bean implementation class. The implementation class is the class specified in the ejb-class element for the specified bean in the ejb-jar.xml deployment descriptor file. Do not map the Home or Remote interface classes, or the primary key classes.

EJBs and the OracleAS TopLink Mapping Workbench

If you use the OracleAS TopLink Mapping Workbench to build projects with entity beans, you can load the bean classes themselves into the OracleAS TopLink Mapping Workbench. You do not need to load the Remote, Local, Home, and localHome interfaces, or the primary key class, nor must you use these classes to define mappings.

To avoid errors when you load the beans, ensure that classes referenced by the entity beans are on the project class path used by the OracleAS TopLink Mapping Workbench project. The Remote, Local, Home, and localHome interfaces must also be on the class path, because they may be used during EJB validation.

Inheritance

Inheritance enables you to share attributes between objects such that a subclass inherits attributes from its parent class. OracleAS TopLink provides several methods to preserve inheritance relationships, and enables you to override mappings that are specified in a superclass, or to map attributes that are not mapped in the superclass. Subclasses must include the same database field (or fields) as the parent class for their primary key (although the primary key can have different names in these two tables). As a result, when you are mapping relationships to a subclass stored in a separate table, the subclass table must include the parent table primary key, even if the subclass primary key differs from the parent primary key.

This section describes OracleAS TopLink inheritance, and introduces several topics and techniques to leverage inheritance in your own applications, including:

For more information about implementing inheritance in code, see "Implementing Inheritance in Java".

Understanding Object Inheritance

Consider a simple database used by a courier company. It contains registration information for three types of vehicles: trucks, cars, and bicycles. For each vehicle type, your application requires the following information:

If these are all the attributes shared by all vehicles in the application, then these attributes must all appear in the super class, Vehicle. You can then build subclasses for each of the vehicle types that reflects their differences. For example, the Truck class might have an attribute indicating whether the local department of transportation considers it to be a commercial vehicle (NumAxles), the Car class might require a NumPass (number of passengers) attribute, and the Bicycle class, by virtue of its more limited range, may require a Location attribute. Through inheritance, each vehicle automatically inherits the basic vehicle information, but by being separate subclasses, also have unique characteristics.

Figure 3-6 Inheritance in a Courier Application

Text description of inhrex.gif follows.

Text description of the illustration inhrex.gif

Representing Inheritance in the Database

You can represent inheritance in the database in one of two ways:

If your database already represents the objects in the inheritance hierarchy this way, you can map the objects and relationships without modifying the tables. However, it is most efficient to represent all classes from a given inheritance hierarchy in a single table, because it substantially reduces the number of table reads and eliminates joins when querying on objects in the hierarchy.

Figure 3-8 Inheritance in the Database in a Single Table

Text description of dbinhrt2.gif follows.

Text description of the illustration dbinhrt2.gif

To consolidate tables in the database this way, determine the class type of the objects represented by the rows in the table. There are two ways to determine class type:

Class Types

The OracleAS TopLink inheritance hierarchy includes three types of classes:

Root Class

The root class stores information for 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, you can also configure the root class to return only instances of itself, without instances of its subclasses when queried. All class types beneath the root class inherit from the root class.

Branch Class

Branch classes have a persistent superclass and subclasses. By default, queries performed on the branch class return instances of the branch class and any of its subclasses. As with the root class, you can configure the branch class to return only instances of itself without instances of its subclasses when queried. All classes below the branch class inherit attributes from the branch class, including any attributes the branch class inherits from classes above it in the hierarchy.

Leaf Class

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

Class Indicators

A class indicator is a mechanism for determining the class or type of an object. For example, a Person table might include an indication of whether the person represented by the table row is an Employee or a Manager. Use the class indicator to select the appropriate subclass to be instantiated from a set of available subclasses.

Class Indicator Field

A class indicator field is a number or string stored in a database table that indicates the class or type of an object. OracleAS TopLink uses this information to determine the correct type of object to instantiate when building an object from that data in the row. For example, an EMPLOYEE table might contain a field, the value of which indicates whether the employee is permanent or contract, and determines whether OracleAS TopLink instantiates a PermanentEmployee object or a ContractEmployee object.

You can use strings or numbers as values in the class indicator field in the database. The root class descriptor must specify how the value in the class indicator field translates into the class to be instantiated.

Class Indicators and Mappings

Class indicator fields do not have an associated direct mapping unless it is set to read-only. Mappings defined for the write-lock or class indicator field must be read-only, unless the write-lock is configured not to be stored in the cache and the class indicator is part of the primary key.

For more information about transformation mappings, see "Transformation Mappings".

Class Extraction Methods

Class extraction enables you to determine the correct class type to instantiate from a table that includes several classes. Unlike a class indicator, however, a class extraction method does not rely on a single column in the table to determine class type. Instead, you can apply logic to the information in several fields to determine class type.

This method is useful when you use a legacy database with a new application. Table 3-1 illustrates a sample use of the a class extraction method.

Table 3-1 Sample use of the a Class Extraction Method
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

The inheritance hierarchy is designed such that Employee is the root class, and Director is a branch class that inherits from Employee. All employees, other than directors, are represented as instances of Employee, but directors must be represented by an instance of the Director class. Because values other than 2 can appear in the JOB_TYPE field, you cannot use OracleAS TopLink's class indicator mechanism for mapping this data.

To resolve this, add a class extraction method to the root class, Employee. The method executes custom logic to determine the correct class to instantiate. The method is static, returns a Class object, and takes DatabaseRow as a single parameter.

Example 3-24 Simple Class Extraction Method

// Return the Director class for TYPE values of 2,
// Employee class for any other value

public static Class getClassFromRow(DatabaseRow row) {
    if (row.get("JOB_TYPE").equals(new Integer(2)) {
        return Director.class;
    }
    else { return Employee.class;
    }
}

This simple case enables you to determine whether the selected person is of the Director class or the Employee class. You can also implement complex logic that combines information from several columns in the table to infer class type. For example, consider a table that represents vehicles in a municipal vehicle pool. In addition to other information, Table 3-2 illustrates, the database includes data that indicates gross vehicle weight and number of axles.

Table 3-2 Gross Vehicle Weight and Number of Axles Example
Gross Vehicle Weight Number Of Axles

2650

3

800

2

2730

2

2400

2

3580

4

Although there is no direct indication of vehicle type in the data, you can build logic into a class extraction method to infer the vehicle type. This is made easier if you are familiar with the available types in the database. In this example, you can use a class extraction method to implement the following logic:

Example 3-25 Complex Class Extraction Method

public static Class getClassFromRow(DatabaseRow row) {
    if (row.get("NumberOfAxles").intValue()>2){
        return HeavyTruck.class;
    }
    else {
        if (row.get("GrossVehicleWeight").intValue()>1000) {
        return PassengerVehicle.class;
        }
        else { return Motorcycle.class;
        }
    }
}

In addition to implementing logic to determine object class, you can also use class extraction methods to execute other methods unrelated to class determination. This is an unusual use for class extraction methods but, provided that the method ultimately returns a class type, it is possible.

To implement the class extraction method in the OracleAS TopLink Mapping Workbench, open the inheritance settings for the root descriptor in the subclass hierarchy (EMPLOYEE in this case), and select the class extraction method in the Use Class Extraction Method box.

Entity Bean Inheritance Restrictions

The following restrictions apply to entity beans when using inheritance:

The Application Server EJB 1.1 and 2.0 CMP Advanced Examples illustrate inheritance. For more information, see the OracleAS TopLink Examples at <ORACLE_HOME>\toplink\doc\examples.htm.


Note:

Because the existing EJB specifications offer no implementation guidelines for inheritance, exercise caution when implementing inheritance, especially if EJB compliance is an issue for your application.


Mapping EJB Entity Beans

EJB Entity beans represent a business entity. Entity beans can be shared by many users and are long-lived, able to survive a server failure. Essentially, entity beans are persistent data objects (objects with durable state that exist from one moment in time to the next).

This section describes entity bean development, as well as the following mapping topics and techniques:

Terminology and Definitions

Enterprise JavaBeans

An EJB implements a business task or a business entity. EJBs are server-side domain objects that fit into a component-based architecture for building enterprise applications using the Java language. EJBs are Java objects that the developer can install in an EJB server to make them distributed, transactional, and secure. OracleAS TopLink supports three kinds of EJBs under the EJB 2.0 specification: session beans, entity beans, and message-driven beans. Note that EJB 1.1 does not support message-driven beans.

EJB Server and Container

An EJB bean resides in an EJB container that in turn resides in an EJB server. Although the EJB 2.0 specification does not define the container-server relationship, the accepted paradigm is that the server provides the bean with access to various services (transactions, security, and so on), and the container provides the execution context for the bean by managing its life cycle.

Deployment Descriptors

Deployment descriptors supply additional information that is required to install an EJB within its server. The deployment descriptors are of a set of XML files that provide the security, transaction, relationship, and persistence information for the bean.

Session Beans

Session beans represent a business operation, task, or process. Although the use of a session bean may involve database access, the beans are not in themselves persistent because they do not directly represent a database entry. Session beans do not always retain conversational state; they can be stateful and retain client information between calls. They can be stateless and retain information only within a single method call.

You can use OracleAS TopLink to make the regular Java objects that are accessed by a session bean persistent, or to access OracleAS TopLink persistent entity beans. Session beans may also act as wrappers to other legacy applications.

Entity Beans

Entity beans represent a persistent data object that exists from one access to the next. Accomplish persistence by storing the object in a relational database, object database, or some other storage facility.

Two schemes exist for making entity beans persistent: bean-managed persistence (BMP) and container-managed persistence (CMP). BMP requires the bean developer to hand-code the methods that perform the persistence work. CMP uses information supplied by the developer to handle all aspects of persistence.

Message-Driven Beans

Message-driven beans process asynchronous Java Message Service (JMS) messages. A bean method is transactionally-invoked by a JMS message sent to the objects registered against the given topic. From a client perspective, a message-driven bean is simply a JMS consumer with no conversational state and no Home or Remote interfaces.

Overview of Bean-Managed Persistence

OracleAS TopLink provides a class oracle.toplink.ejb.bmp.BMPEntityBase. This class provides developers with a starting point when developing beans. The BMPEntityBase class provides implementation for all EJB specification required methods except ejbPassivate() which is excluded because of special requirements. By subclassing the BMPEntityBase, developers have an OracleAS TopLink-enabled entity bean.

To use the BMPEntityBase, create the sessions.xml file. For information about the sessions.xml file, see "Session Manager". In addition, add an oracle.toplink.ejb.bmp.BMPWrapperPolicy to each descriptor that represents an Entity Bean. This BMPWrapperPolicy provides OracleAS TopLink with the information to create Remote objects for entity beans and to extract the data out of a Remote object. After this is performed, the user must create the Home and Remote interfaces, create deployment descriptors, and deploy the beans.

If a more customized approach is required, OracleAS TopLink provides a hook into its functionality through the oracle.toplink.ejb.bmp.BMPDataStore class. Use this class to translate EJB-required functionality into simple calls.

The BMPDataStore provides implementations of LOAD and STORE, multiple finders, and REMOVE functionality. The BMPDataStore, requires a sessions.xml file and the session manager. A single instance of BMPDataStore must exist for each bean type deployed within a session. When creating a BMPDataStore, pass in the session name of the session that the BMPDataStore must use to persist the beans and the class of the Bean type being persisted. Store the BMPDataStore in a global location so that each instance of a Bean type uses the correct Store.

If you use a customized implementation, the full functionality of the server session and the UnitOfWork is available.

BMP Support with EJB 2.0

To use BMP support with EJB 2.0, the Home interface must inherit from the oracle.toplink.ejb.EJB20Home. To make calls to the oracle.toplink.ejb.bmp.BMPEntityBase, the FindAll() method must call the EJB 2.0 version of the methods. These methods are prefixed with ejb20.

For example, in the EJB 2.0 version, the findAll() method appears as ejb20FindAll.

Using Local beans

To use local beans, use the oracle.toplink.ejb.EJB20LocalHome setting instead of the default oracle.toplink.ejb.EJB20Home.

Instead of the oracle.toplink.ejb.BMPWrapperPolicy setting, use the oracle.toplink.ejb.bmp.BMPLocalWrapperPolicy setting.

To accommodate both local and remote configurations, ensure the following:

Overview of Container-Managed Persistence

OracleAS TopLink CMP is an extension of the OracleAS TopLink persistence framework. OracleAS TopLink CMP support provides container-managed persistence for EJBs deployed in a J2EE container.

OracleAS TopLink CMP support enables complex mappings from entity beans to relational database tables and enables you to model bean-to-bean and bean-to-regular Java object relationships. OracleAS TopLink provides a rich set of querying options and allows query definition at the bean-level rather than the database level. OracleAS TopLink CMP supports the specification as defined by Sun Microsystems.

Understanding CMP

This section introduces the concepts required to use CMP facilities. It highlights the features that are specific to OracleAS TopLink CMP, and explains any differences in the use of other core features.

OracleAS TopLink and CMP Entity Beans

The common mechanism for developers to make beans persistent is to map beans to a relational database. The EJB specification describes the CMP entity bean as a type of bean for which the designer does not have to include calls to any particular persistence mechanism in the bean itself. The EJB Server and its tools use meta-information in the deployment descriptor to describe how the bean is to be persisted to a database. This is commonly referred to as automatic persistence.

EJB 2.0 Support

OracleAS TopLink provides support for EJB 2.0 entity beans. Here are some specific features of EJB 2.0 that OracleAS TopLink supports:

Java Objects and Entity Beans

Table 3-3 illustrates the components that Java objects contain:

Table 3-3 Java Object Components
Component Function

Attributes

Stores primitive data such as integers, as well as simple Java types such as String and Date.

Relationships

Stores references to other OracleAS TopLink-enabled classes. An OracleAS TopLink-enabled class (also known as a persistent class) has a descriptor and can be stored in the database.

Methods

Stores paths of execution that can be invoked in a Java environment. Methods are not stored in the database.

Table 3-4 illustrates the components that entity beans contain:

Table 3-4 Entity Bean Components  
Component Function

Bean instance

An instance of an entity bean class supplied by the bean developer. It is a regular Java object whose class implements the javax.ejb.EntityBean interface. The bean instance has persistent state. The client application must never access the bean instance directly.

EJBObject

An instance of a generated class that implements the Remote interface defined by the bean developer. This instance wraps the bean and provides client interaction with the bean. The EJBObject does not have persistent state.

EJBHome

An instance of a class that implements the Home interface supplied by the bean developer. This instance, accessible from JNDI, provides all create and finder methods for the EJB. The EJBHome does not have persistent state.

EJBLocalObject
(EJB 2.0 only)

An instance of a generated class that implements the Local interface defined by the bean developer. The key difference between an EJBLocalObject and an EJBObject is that the EJBLocalObject is accessed only from within the same server on which the beans are deployed. The EJBLocalObject does not have persistent state.

EJBLocalHome
(EJB 2.0 only)

An instance of a class that implements the localHome interface supplied by the bean developer. This instance, accessible from JNDI, provides all create and finder methods for the EJB. The key difference between an EJBLocalHome and an EJBHome is that access to the EJBLocalHome is only available from within the same server on which the beans are deployed, even when using JNDI. The EJBLocalHome does not have persistent state.

EJB Primary Key

An instance of the primary key class provided by the bean developer. The primary key is a serializable object whose fields match the primary key fields in the bean instance. Although the EJB primary key shares some data with the bean instance, it does not have persistent state. If the key consists of a single field, the bean does not have to have a separate primary key class under the EJB 1.1 or later specifications.

For more information about the Enterprise JavaBeans and the EJB specification, see

http://java.sun.com/products/ejb/
http://java.sun.com/products/ejb/docs.html
http://java.sun.com/j2ee/white/index.html

Maintaining Bidirectional Relationships

When one-to-one or many-to-many mappings are bidirectional, maintain the back-pointers as the relationships change. When the relationship is between two entity beans (in EJB 2.0), OracleAS TopLink automatically maintains the relationship. However, when the relationship is between an entity bean and a Java object, or when the application is built to the EJB 1.1 specification, the relationship must be maintained manually. To set the back-pointer under the EJB 1.1 specification, do one of the following:

If you set back-pointers within the entity bean, the client is freed of this responsibility. This has the advantage of encapsulating the mapping maintenance implementation in the bean.


Note:

Under the EJB 1.1 specification, you must manually update all back-pointers.


One-to-Many Relationship

In a one-to-many mapping, a source bean might have several dependent target objects. For example, an EmployeeBean might have several dependent phoneNumbers. When a new dependent object (a phoneNumber, in this example) is added to an employee record, the phoneNumber's back-pointer to its owner (the employee) must also be set.

Example 3-26 Setting the Back-Pointer in the Entity Bean

Maintaining a one-to-many relationship in the entity bean involves getting the local object reference from the context of the EmployeeBean, and then updating the back-pointer. The following code illustrates this technique:

// obtain owner and phoneNumber
owner = empHome.findByPrimaryKey(ownerId); 
phoneNumber = new PhoneNumber("cell", "613", "5551212");
// add phoneNumber to the phoneNumbers of the owner
owner.addPhoneNumber(phoneNumber); 

The Employee's addPhoneNumber( ) method maintains the relationship, as follows:

public void addPhoneNumber(PhoneNumber newPhoneNumber) {
    //get, then set the back pointer to the owner
    Employee owner = (Employee)this.getEntityContext().getEJBLocalObject();
    newPhoneNumber.setOwner(owner);	
    //add new phone
    getPhoneNumbers().add(newPhoneNumber);
}

Managing Dependent Objects Under EJB 1.1

The EJB 1.1 specification recommends that you model entity beans so that all dependent objects are regular Java objects and not other entity beans. If you expose a dependent or privately owned object to the client application, it must be serializable (that is, it implements the java.io.Serializable interface) so that it can be sent over to the client and back to the server.

Serializing Java Objects Between Client and Server

Because entity beans are remote objects, they are referenced remotely in a pass-by-reference fashion. When an entity bean is returned to the client, a remote reference to the bean is returned.

Unlike entity beans, regular Java objects are not remote objects. As a result, when regular Java objects are referenced remotely, they are passed by value (rather than by reference) and serialized (copied) from the remote machine on which they originally resided.

Merging Changes to Regular Java Objects

One of the effects of serializing regular Java objects between servers and clients is a loss of object identity, due to the copying semantics inherent in serialization. When you serialize a dependent object from the server to the client and then back, two objects with the same primary key but different object identities exist in the server cache. These objects must be merged to avoid exceptions.

If relationships exist between entity beans and Java objects, and these objects are serialized back and forth between the client and server, either:

Using Session Accessor to Merge Dependent Objects

Use the class oracle.toplink.ejb.WebLogic.SessionAccessor to perform merges for you within the set methods (on your bean class) that take regular Java objects as their arguments.

Two static methods are defined on the SessionAccessor that allow you to do the register and merge operation:

registerOrMergeObject()

This method requires two arguments: the object to merge and the EntityContext for the bean.

Example 3-27 Using the registerOrMergeObject() Method

public void setAddress(Address address) {
    this.address = (Address)SessionAccessor
      .registerOrMergeObject(address,this.ctx);
}

The registerOrMergeAttribute() method requires three arguments: the Java object to be merged, the name of the attribute, and the EntityContext for the bean:

public void setAddress(Address address) {
    this.address = (Address) SessionAccessor.registerOrMergeAttribute
      (address, "address", this.ctx);
}

To use the registerOrMergeAttribute() call for collection mappings, pass the entire collection as the attribute object.

For example:

public void setPhones(Vector phones) {
    this.phones = (Vector)SessionAccessor.registerOrMergeAttribute(phones,
      "phones", this.ctx);
    //... additional logic to set back-pointers on the phones
}

The registerOrMergeObject() method is not as simple to use for setters of collection mappings. It requires that you iterate through the collection and invoke the registerOrMergeObject() for each element in the collection. You must also create a new collection, set in the entity bean, to hold the return values of the call.

Merging code may be required in methods that add elements to a collection.

For example:

/* The old version of this phone number is removed from the collection. It is 
assumed that equals() returns true for phones with the same primary key value. 
If this is not true, you must iterated through the phones to see if a phone with 
the same primary key already exists in the collection. */
public void addPhoneNumber(PhoneNumber phone) {
phone.setOwner((Employee)this.ctx.getEJBObject());
//add to collection
//merge new phone
PhoneNumber serverSidePhone = 
(PhoneNumber)SessionAccessor.registerOrMergeObject(phone,this.ctx);
//set back pointer
getPhoneNumbers().addElement(serverSidePhone);
}}


Note:

This example requires merging code only if there is a risk that a Phone with the same primary key can be added twice. If the elements in a collection cannot be added more than once, then merging code is not required.


Merging Dependent Objects without Session Accessor

There are several ways to merge objects manually. For example, you can use a set() method as follows:

public void setAddress(Address address) {
    if(this.address == null){
        this.address = address;
    } else{
        this.address.merge(address);
    }
}

You must merge objects when they are added to a collection on the entity bean, unless the objects cannot be added more than once to a collection in which case, merging is not necessary.

Merging a collection requires more work. Determine if a copy of each object already exists in the collection, and if so, merge the two copies. If not, you need only add the new object to the collection.

Managing Dependent Objects Under EJB 2.0

Unlike EJBs, OracleAS TopLink dependent persistent objects can be sent back and forth between a client and the server. When objects are serialized, the risk exists the objects can cause the cache to lose the identity of the objects or attempt to cache duplicate identical objects. To avoid potential problems, use the bean set methods when adding dependent objects to relationship collections. This enables OracleAS TopLink to handle merging of objects in the cache.

Example 3-28 Adding a Dependent Object

addPhoneNumber(PhoneNumber phone) { 
    Collection phones = this.getPhoneNumbers(); 
    Vector newCollection = new Vector();
    newCollection.addAll(phones); 
    newCollection.add(phone); 
    this.setPhones(newCollection); 
}

Managing Collections of EJBObjects Under EJB 1.1

Collections generally use the equals() method to compare objects. However, in the case of a Java object that contains a collection of entities, the EJBObjects do not respond as expected to the equals() method. If you manage a collection of entities under EJB 1.1, we recommend the use of the isIdentical() method to avoid problems.

In addition, the standard collection methods, such as remove() or contains(), frequently return unexpected results and so must be avoided.


Note:

This issue does not arise in the case of an entity contains a collection of entities, because the EJB 2.0 container collection used handles equality appropriately.


Several options are available when dealing with collections of EJBObjects. One option is to create a helper class to assist with collection-type operations. Example 3-29 illustrates the use of a helper in the EJBCollectionHelper distribution.

Example 3-29 Using a Helper Class to Manage a Collection of EJBObjects

public void removeOwner(Employee previousOwner){ 
EJBCollectionHelper.remove(previousOwner, getOwners());
} 

Example 3-30 illustrates the implementation of remove() and indexOf() in EJBCollectionHelper.

Example 3-30 Using remove() and indexOf() in the EJBCollectionHelper

public static boolean remove(javax.ejb.EJBObject ejbObject, Vector vector) { 
    int index = -1; 
    index = indexOf(ejbObject, vector); 
    // indexOf returns -1 if the element is not found. 
    if(index == -1){ 
        return false; 
    } 
    try{ 
        vector.removeElementAt(index); 
    } catch(ArrayIndexOutOfBoundsException badIndex){ 
        return false; 
    } 
    return true; 
} 
public static int indexOf(javax.ejb.EJBObject ejbObject, Vector vector) { 
    Enumeration elements = vector.elements(); 
    boolean found = false; 
    int index = 0; 
    javax.ejb.EJBObject current = null; 
    while(elements.hasMoreElements()){ 
        try{ 
            current = (javax.ejb.EJBObject) 
            elements.nextElement(); 
            if(ejbObject.isIdentical(current)){ 
            found = true; 
            break; 
            } 
        }catch(ClassCastException wrongTypeOfElement){ 
            . . . 
        }catch (java.rmi.RemoteException otherError){ 
            . . . 
        } 
        index++; //increment index counter 
    } 
    if(found){ 
        return index; 
    } else{ 
        return -1; 
    } 
} 

You can create a special Collection class that uses isIdentical() instead of equals() for its comparison operations. To use isIdentical(), properly define the equals() method for the primary key class.

Descriptor Validation

You can validate descriptors in two ways:

Advanced Mappings

Several complex mappings are available in OracleAS TopLink. This section discusses the following mapping types:

Transformation Mappings

Transformation mappings enable you to create specialized translations between how a value is represented in Java and in the database. Use transformation mappings only when mapping multiple fields into a single attribute. Transformation mapping is often appropriate when you use values from multiple fields to create an object.


Note:

Because of the complexity of transformation mappings, it is often easier to perform the transformation with get and set methods of a direct-to-field mapping.


After you create the required transformation method, use the OracleAS TopLink Mapping Workbench to implement transformation mappings.

For more information, see "Working with Transformation Mappings" in Oracle Application Server TopLink Mapping Workbench User's Guide.

Implementing Transformation Mappings in Java

Transformation mappings are instances of the TransformationMapping class and require the following elements:

Use the optional setGetMethodName( ) and setSetMethodName( ) messages to access the attribute through user-defined methods, rather than directly.

Example 3-31 Creating a Transformation Mapping and Registering It with the Descriptor

This example provides custom support for two fields. You can use this approach to map any number of fields.

// Create a new mapping and register it with the descriptor.
TransformationMapping transformation1 = new TransformationMapping();
transformation1.setAttributeName ("dateAndTimeOfBirth");
transformation1.setAttributeTransformation ("buildDateAndTime");
transformation1.addFieldTransformation("B_DAY", "getDateOfBirth");
transformation1.addFieldTransformation("B_TIME", "getTimeOfBirth");
descriptor.addMapping(transformation1);
// Define attribute transformation method to read from the database row
public java.util.Date buildDateAndTime(DatabaseRow row) {
    java.sql.Date sqlDateOfBirth = (java.sql.Date)row.get("B_DAY");
    java.sql.Time timeOfBirth = (java.sql.Time)row.get("B_TIME");
    java.util.Date utilDateOfBirth = new java.util.Date(
        sqlDateOfBirth.getYear(),
        sqlDateOfBirth.getMonth(),
        sqlDateOfBirth.getDate(),
        timeOfBirth.getHours(),
        timeOfBirth.getMinutes(),
        timeOfBirth.getSeconds());
    return utilDateOfBirth;
}

// Define a field transformation method to write to the database
public java.sql.Time getTimeOfBirth()
{
    return new java.sql.Time this.dateAndTimeOfBirth.getHours(),
       this. dateAndTimeOfBirth.getMinutes(),
      this.dateAndTimeOfBirth.getSeconds()); 
}

// Define a field transformation method to write to the database
public java.sql.Date getDateOfBirth()
{
    return new java.sql.DateOfBirth this.dateAndTimeOfBirth.getYear(),
      this.dateAndTimeOfBirth.getMonth(), this.dateAndTimeOfBirth.getDate());
}

Example 3-32 Creating a Transformation Mapping Using Indirection

// Create a new mapping and register it with the descriptor.
TransformationMapping transformation2 = new 
transformation2.setAttributeName("designation");
transformation2.setGetMethodName ("getDesignationHolder");
transformation2.setSetMethodName ("setDesignationHolder");
transformation2.setAttributeTransformation ("getRankFromRow");
transformation2.addFieldTransformation("RANK", "getRankFromObject");
transformation2.useIndirection();
descriptor.addMapping(transformation2);
//Define an attribute transformation method to read from database row.
public String getRankFromRow() 
{
    Integer value = new Integer(((Number)row.get("RANK)).intValue());
    String rank = null;
    if (value.intValue() == 1) {
        rank = "Executive";
    }
    if (value.intValue() == 2) {
        rank = "Non-Executive";
    }
    return rank;
}
//Define a field transformation method to write to the database.
public Integer getRankFromObject()
{
    Integer rank = null;

    if (getDesignation().equals("Executive")) rank = new Integer(1);
    if (getDesignation().equals("Non-Executive")) rank = new Integer(2);
    return rank;
}

//Provide accessor methods for the indirection.
private ValueHolderInterface designation;
public ValueHolderInterface getDesignationHolder()
{
    return designation;
}
public void setDesignationHolder(ValueHolderInterface value)
{
    designation = value;
}

For more information about the available methods for TransformationMapping, see the Oracle Application Server TopLink API Reference.

Serialized Object Mappings

Serialized object mappings are used to store large data objects, such as multimedia files and BLOBs, in the database. Serialization transforms these large objects as a stream of bits.

Serialized object mappings are instances of the SerializedObjectMapping class and require the following elements:

Use the optional setGetMethodName( ) and setSetMethodName( ) messages to access the attribute through user-defined methods, rather than directly. You do not have to define accessors when you use Java 2.

Example 3-33 Creating a Serialized Object Mapping and Registering It with the Descriptor

// Create a new mapping and register it with the descriptor.
SerializedObjectMapping serializedMapping = new SerializedObjectMapping();
serializedMapping.setAttributeName("jobDescription");
serializedMapping.setFieldName("JOB_DESC");
descriptor.addMapping(serializedMapping);

For more information about the available methods for SerializedObjectMapping, see the Oracle Application Server TopLink API Reference.

Variable One-to-One Mappings

Variable one-to-one mappings are instances of the VariableOneToOneMapping() class and require the following elements:

If the mapping uses a class indicator field:

Example 3-34 Defining a Variable One-to-One Mapping Using a Class Indicator Field

VariableOneToOneMapping variableOneToOneMapping = new VariableOneToOneMapping();
variableOneToOneMapping.setAttributeName("contact");
variableOneToOneMapping.setReferenceClass (Contact.class); 
variableOneToOneMapping.setForeignQueryKeyName ("C_ID", "id");
variableOneToOneMapping.setTypeFieldName("TYPE");
variableOneToOneMapping.addClassIndicator(Email.class, "Email");
variableOneToOneMapping.addClassIndicator(Phone.class, "Phone");
variableOneToOneMapping.dontUseIndirection();
variableOneToOneMapping.privateOwnedRelationship();

Example 3-35 Defining a Variable One-to-One Mapping Using a Primary Key

VariableOneToOneMapping variableOneToOneMapping = new VariableOneToOneMapping();
variableOneToOneMapping.setAttributeName("contact");
variableOneToOneMapping.setReferenceClass (Contact.class);
variableOneToOneMapping.setForeignQueryKeyName ("C_ID", "id");
variableOneToOneMapping.dontUseIndirection();
variableOneToOneMapping.privateOwnedRelationship();

For more information about the available methods for VariableOneToOneMapping, see the Oracle Application Server TopLink API Reference.

Object Relational Mappings

Relational mappings defines the reference between persistent objects. Object relational mappings enable you to persist an object model into an object-relational data model. The OracleAS TopLink Mapping Workbench does not directly support these mappings--you must define them in code through amendment methods.

OracleAS TopLink supports the following object-relational mappings:

Array Mappings

In an object-relational data-model, structures can contain arrays (collections of other data types). These arrays can contain primitive data types or collections of other structures. OracleAS TopLink stores the arrays with their parent structure in the same table.

All elements in the array must be the same data type. The number of elements in an array controls the size of the array. An Oracle database allows arrays of variable sizes (called Varrays).

Oracle8i or higher offers two collection types:

OracleAS TopLink supports arrays of primitive data through the ArrayMapping class. This is similar to DirectCollectionMapping--it represents a collection of primitives in Java. However, the ArrayMapping class does not require an additional table to store the values in the collection.

OracleAS TopLink supports arrays of aggregate structures through the ObjectArrayMapping class.

OracleAS TopLink supports nested tables through the NestedTableMapping class.

Implementing Array Mappings in Java

Array mappings are instances of the ArrayMapping class and require the following elements:

Example 3-36 Creating an Array Mapping for the Employee Source Class and Registering It with the Descriptor

// Create a new mapping and register it with the source descriptor.
ArrayMapping arrayMapping = new ArrayMapping();
arrayMapping.setAttributeName("responsibilities");
arrayMapping.setStructureName("Responsibilities_t");
arrayMapping.setFieldName("RESPONSIBILITIES");
descriptor.addMapping(arrayMapping);

In addition to the API Example 3-36 illustrates, other common API for use with implement array mapping include:

For more information about the available methods for ArrayMapping, see the Oracle Application Server TopLink API Reference.

Object Array Mappings

In an object-relational data-model, object arrays allow for an array of object types or structures to be embedded into a single column in a database table or an object table.

OracleAS TopLink supports object array mappings to define a collection-aggregated relationship in which the target objects share the same row as the source object.

Implementing Object Array Mappings in Java

Object array mappings are instances of the ObjectArrayMapping class. You must associate this mapping to an attribute in the parent class. Object array mappings require the following elements:

Use the optional setGetMethodName( ) and setSetMethodName( ) messages to access the attribute through user defined methods, rather than directly.

Example 3-37 Creating an Object Array Mapping for the Insurance Source Class and Registering It with the Descriptor

// Create a new mapping and register it with the source descriptor.
ObjectArrayMapping phonesMapping = new ObjectArrayMapping();
phonesMapping.setAttributeName("phones");
phonesMapping.setGetMethodName("getPhones");
phonesMapping.setSetMethodName("setPhones");
phonesMapping.setStructureName("PHONELIST_TYPE");
phonesMapping.setReferenceClass(Phone.class);
phonesMapping.setFieldName("PHONES");
descriptor.addMapping(phonesMapping);

For more information about the available methods for ObjectArrayMapping, see the Oracle Application Server TopLink API Reference.

Structure Mappings

In an object-relational data-model, structures are user defined data types or object-types. This is similar to a Java class--it defines attributes or fields in which each attribute is either:

OracleAS TopLink maps each structure to a Java class defined in your object model and defines a descriptor for each class. A StructureMapping maps nested structures, similar to an AggregateObjectMapping. However, the structure mapping supports null values and shared aggregates without requiring additional settings (because of the object-relational support of the database).

Implementing Structure Mappings in Java

Structure mappings are instances of the StructureMapping class. You must associate this mapping to an attribute in each of the parent classes. Structure mappings require the following elements:

Use the optional setGetMethodName( ) and setSetMethodName( ) messageto access the attribute through user-defined methods, rather than directly.

Make the following changes to the target (child) class descriptor:

Example 3-38 Creating a Structure Mapping for the Employee Source Class and Registering It with the Descriptor

// Create a new mapping and register it with the source descriptor.
StructureMapping structureMapping = new StructureMapping();
structureMapping.setAttributeName("address");
structureMapping.setReferenceClass(Address.class); 
structureMapping.setFieldName("address");
descriptor.addMapping(structureMapping);

Example 3-39 Creating the Descriptor of the Address Aggregate Target Class

The aggregate target descriptor does not need a mapping to its parent, or any table or primary key information.

// Create a descriptor for the aggregate class. The table name and primary key 
are not specified in the aggregate descriptor.
ObjectRelationalDescriptor descriptor = new ObjectRelationalDescriptor ();
descriptor.setJavaClass(Address.class);
descriptor.setStructureName("ADDRESS_T");
descriptor.descriptorIsAggregate();

// Define the field ordering
descriptor.addFieldOrdering("STREET");
descriptor.addFieldOrdering("CITY");
...

// Define the attribute mappings or relationship mappings.
...

In addition to the API Example 3-39 illustrates, other common API for use with structure mapping include:

For more information about the available methods for StructureMapping, see the Oracle Application Server TopLink API Reference.

Reference Mappings

In an object-relational data-model, structures reference each other through refs--not through foreign keys (as in a traditional data model). Refs are based on the target structure's ObjectID.

OracleAS TopLink supports refs through the ReferenceMapping. They represent an object reference in Java, similar to a OneToOneMapping. However, the reference mapping does not require foreign key information.

Implementing Reference Mappings in Java

Reference mappings are instances of the ReferenceMapping class. You must associate this mapping to an attribute in the source class. Reference mappings require the following elements:

Use the optional setGetMethodName( ) and setSetMethodName( ) messages to access the attribute through user-defined methods, rather than directly.

Example 3-40 Creating a Reference Mapping for the Employee Source Class and Registering It with the Descriptor

// Create a new mapping and register it with the source descriptor.
ReferenceMapping referenceMapping = new ReferenceMapping();
referenceMapping.setAttributeName("manager");
referenceMapping.setReferenceClass(Employee.class);
referenceMapping.setFieldName("MANAGER");
descriptor.addMapping(refrenceMapping);

In addition to the API Example 3-40 illustrates, other common API for use with reference mappings include:

For more information about the available methods for ReferenceMapping, see the Oracle Application Server TopLink API Reference.

Nested Table Mappings

Nested table types model an unordered set of elements. These elements may be built-in or user-defined types. You can view a nested table as a single-column table or, if the nested table is an object type, as a multi-column table (with a column for each attribute of the object type).

Nested tables represent a one-to-many or many-to-many relationship of references to another independent structure. They support querying and joining better than Varrays that are inlined to the parent table.

OracleAS TopLink supports nested table through the NestedTableMapping. They represent a collection of object references in Java, similar to a OneToManyMapping or ManyToManyMapping. However, the nested table mapping does not require foreign key information (such as a one-to-many mapping) or the relational table (such as a many-to-many mapping).

Implementing Nested Table Mappings in Java

Nested table mappings are instances of the NestedTableMapping class. This mapping is associated to an attribute in the parent class. Nested table mapping require the following elements:

Use the optional setGetMethodName( ) and setSetMethodName( ) messages to allow OracleAS TopLink to access the attribute through user-defined methods, rather than directly.

Example 3-41 Creating a Nested Table Mapping for the Insurance Source Class and Registering It with the Descriptor

// Create a new mapping and register it with the source descriptor.
NestedTableMapping policiesMapping = new NestedTableMapping();
policiesMapping.setAttributeName("policies");
policiesMapping.setGetMethodName("getPolicies");
policiesMapping.setSetMethodName("setPolicies");
policiesMapping.setReferenceClass(Policy.class);
policiesMapping.dontUseIndirection();
policiesMapping.setStructureName("POLICIES_TYPE");
policiesMapping.setFieldName("POLICIES");
policiesMapping.privateOwnedRelationship();
policiesMapping.setSelectionSQLString("select p.* from policyHolders ph,
  table(ph.policies) t, policies p where ph.ssn=#SSN and ref(p) = value(t)");
descriptor.addMapping(policiesMapping);

In addition to the API Example 3-41 illustrates, other common API for use with nested table mappings include:

For more information about the available methods for NestedTableMapping, see the Oracle Application Server TopLink API Reference.

Direct Map Mappings

Direct map mappings store instances that implement java.util.Map. Unlike one-to-many or many-to-many mappings, the keys and values of the map in this type of mapping are Java objects that do not have descriptors. The object type stored in the key and the value of direct map are Java primitive wrapper types such as String objects.

Support for primitive data types such as int is not provided because Java maps only hold objects.

Direct map mappings are instances of the DirectMapMapping class and require the following elements:

Example 3-42 Creating a Simple Direct Map Mapping

DirectMapMapping directMapMapping = new DirectMapMapping();
directMapMapping.setAttributeName("cities");
directMapMapping.setReferenceTableName("CITY_TEMP");
directMapMapping.setReferenceKeyFieldName("RECORD_ID");
directMapMapping.setDirectKeyFieldName("CITY");
directMapMapping.setDirectFieldName("TEMPERATURE");
directMapMapping.setKeyClass(String.class);
directMapMapping.setValueClass(Integer.class);

descriptor.addMapping(directMapMapping);

In addition to the API Example 3-42 illustrates, other common API for use with direct map mappings include:

For more information about the available methods for DirectMapMapping, see the Oracle Application Server TopLink API Reference.

Customizing the Project

OracleAS TopLink projects, descriptors, and mapping are normally created using the OracleAS TopLink Mapping Workbench. The output of the OracleAS TopLink Mapping Workbench is an XML file that contains the mapping information required to store persistent objects in the database.

The OracleAS TopLink Mapping Workbench does not offer access to all the customization available to the OracleAS TopLink descriptors that make up the project. In these situations, to customize the mapping information, you can specify an amendment method to be run at deployment time. Each OracleAS TopLink descriptor can have an amendment method.

This section describes some of the available customization topics and techniques, including:

Customizing OracleAS TopLink Descriptors with Amendment Methods

Amendment methods are static methods that run at deployment time and enable you to implement descriptor customization code. You can modify the OracleAS TopLink descriptor of any persistent class with an amendment method when you first instantiate the descriptor. For container-managed persistence, this happens when the entity beans are deployed into the EJB server.

For more information about amendment methods, see the Oracle Application Server TopLink Mapping Workbench User's Guide.

Using After Load Methods

Some OracleAS TopLink features cannot be configured from the OracleAS TopLink Mapping Workbench. To use these features, amend the descriptor after it is loaded as part of the project. After load methods are a type of amendment method that enables you to modify descriptors in code after you create the project object (either from an XML project or a project class).

To access descriptors from the project object or the session object (after the session object is created from the project), write a Java method that takes the name of the descriptor as a single parameter. You can then send messages to the descriptor or any of its specific mappings to configure advanced features. Make all descriptor changes before the session logs in. Any descriptor change made after login is ignored.

For more information, see "Amending Descriptors After Loading in Oracle Application Server TopLink Mapping Workbench User's Guide.

Use any of the following APIs to implement after load methods:

For more information about these APIs, see the Oracle Application Server TopLink API Reference.

Descriptor Events

The descriptor event manager enables you to create events that trigger other events in your application. You use the Event Manager to invoke specific events when OracleAS TopLink reads, updates, deletes, or inserts objects on the database.

Descriptor events enable you to:

You specify descriptor events in the OracleAS TopLink Mapping Workbench.

For more information, see "Specifying Events" in the Oracle Application Server TopLink Mapping Workbench User's Guide.

Receiving Descriptor Events

Applications receive descriptor events in several ways:

Implement the Descriptor Event Listener Interface

Register objects that implement the DescriptorEventListener interface with the descriptor event manager. The descriptor event manager then notifies the object when any event occurs for that descriptor.

Subclass the Descriptor Event Adapter Class

Use the DescriptorEventAdapter class if your application does not require all the methods defined in the DescriptorEventListener interface. The DescriptorEventAdapter implements the DescriptorEventListener interface and defines an empty method for each method in the interface. To use the adapter, subclass it and then register your new object with the descriptor event manager.

Register an Event Method with a Descriptor

Register a public method as an event method. The descriptor then calls the event method when a database operation occurs. The event method must:

Registering Descriptor Event Listeners

If you want an object other than the domain object to handle these events, register it as a listener with the descriptor event manager. If you want a LockManager to receive events for all Employees, then modify your descriptor amendment to register the LockManager as the listener.

Any object you register as a listener must implement the DescriptorEventListener interface. The amendment method appears in Example 3-43.

Example 3-43 Registering a Descriptor Event Listeners

public static void addToDescriptor(Descriptor descriptor)
{
    descriptor.getEventManager().addListener(LockManager.activeManager());
}
Reference

Table 3-5 summarizes the most common public methods for DescriptorEventManager. For more information about the available methods for DescriptorEventManager, see the Oracle Application Server TopLink API Reference.

Table 3-5 Elements for the Descriptor Event Manager  
Element Default Method Name

Events selectors (Defaults specified in listener interface implementation)

All events take DescriptorEvent:

postBuild
postRefresh
preWrite
postWrite
preDelete
postDelete
preInsert
postInsert
preUpdate
postUpdate
aboutToInsert
aboutToUpdate
postClone
postMerge

All events take (String methodName):

setPostBuildSelector
setPostRefreshSelector
setPreWriteSelector
setPostWriteSelector
setPreDeleteSelector
setPostDeleteSelector
setPreInsertSelector
setPostInsertSelector
setPreUpdateSelector
setPostUpdateSelector
setAboutToInsertSelector
setAboutToUpdateSelector
setPostCloneSelector
setPostMergeSelector

Listener registration Descriptor-Event reference (available methods on Descriptor-Event)

Source object if it implements the listener interface only; aboutToInsert/ Update, / Build only; postMerge / Clone / write events within a Unit of Work

addListener 
(DescriptorEventListener 
listener)
getSource()
getSession()
getQuery()
getDescriptor()
getRow()
getOriginalObject()

Supported Events

The DescriptorEventManager supports several methods, including those in Table 3-6.

Table 3-6 Supported Events  
Triggering Method Type Supported Events Description

Post-X Method

Post-Build

Occurs after an object is built from the database.

Post-X Method

Post-Clone

Occurs after an object has been cloned into a Unit of Work.

Post-X Method

Post-Merge

Occurs after an object has been merged from a Unit of Work.

Post-X Method

Post-Refresh

Occurs after an object is refreshed from the database.

Updating Method

Pre-Update

Occurs before an object is updated in the database. This may be called in a Unit of Work even if the object has no changes and does not require an update.

Updating Method

About-to-Update

Occurs when an object's row is updated in the database. This method is called only if the object has changes in the Unit of Work.

Updating Method

Post-Update

Occurs after an object is updated in the database. This may be called in a Unit of Work even if the object has no changes and does not require an update.

Inserting Method

Pre-Insert

Occurs before an object is inserted in the database.

Inserting Method

About-to-Insert

Occurs when an object's row is inserted in the database.

Inserting Method

Post-Insert

Occurs after an object is inserted into the database.

Writing Method

Pre-Write

Occurs before an object is inserted or updated into the database. This occurs before Pre-Insert/Update.

Writing Method

Post-Write

Occurs after an object is inserted or updated into the database. This occurs after Pre-Insert/Update.

Deleting Method

Pre-Delete

Occurs before an object is deleted from the database.

Deleting Method

Post-Delete

Occurs after an object is deleted from the database.

Descriptor Copy Policy

The OracleAS TopLink Unit of Work feature uses copies of object (clones) rather than the original objects to perform its tasks. You can construct clones as follows:

The most common way to use any policy other than the default (using the object default constructor) is to create an amendment method and specify it in the OracleAS TopLink Mapping Workbench when you configure the class.

For more information about amendment methods, see "Customizing OracleAS TopLink Descriptors with Amendment Methods".

For more information about implementing descriptor copy policy in code, see "Setting the Copy Policy in Java".

Descriptor Query Manager

You can add queries to a descriptor (named queries) for execution later in the application. For example, you can add the following code to a descriptor:

ReadObjectQuery aQuery = new ReadObjectQuery(Employee.class);
descriptor.getQueryManager().addQuery("readAnEmployee", aQuery);

You can accomplish this with an amendment method.

For more information about amendment methods, see "Customizing OracleAS TopLink Descriptors with Amendment Methods".

Replacing Descriptor Queries

You can replace all queries in an OracleAS TopLink descriptor with user defined queries. Doing this enables you to change query behavior or to substitute stored procedures for the queries.

Example 3-44 Substituting a Stored Procedure for a Query

This example illustrates how to force the read object descriptor call to use a stored procedure.

ReadObjectQuery query = new ReadObjectQuery();
StoredProcedureCall call = new StoredProcedureCall();
call.setProcedureName("READ_RDM_EMP");
query.setCall(call);
descriptor.getQueryManager().setReadObjectQuery
(query);

Instantiation Policy

Overriding the Instantiation Policy Using Java Code

The Descriptor class provides the following methods to specify how objects get instantiated:

Setting the Wrapper Policy Using Java Code

The Descriptor class provides methods used in conjunction with the wrapper policy:

Creating EJB Projects and OracleAS TopLink Descriptors in Java

Create mappings and OracleAS TopLink descriptors to access features that are not available in the OracleAS TopLink Mapping Workbench.

To define a project using Java code:

  1. Implement a project class that extends the oracle.toplink.sessions.Project class.

  2. Compile the project class.

  3. Edit the toplink-ejb-jar.xml deployment descriptor so that the value for the project-class element is the fully-qualified Project class name.

    For more information about creating project classes, see the Oracle Application Server TopLink Mapping Workbench User's Guide.


    Note:

    Use the OracleAS TopLink Mapping Workbench to create a Java Project class from an existing project. This provides a starting point for a custom project class. For more information, see the Oracle Application Server TopLink Mapping Workbench User's Guide.

    You can also use the OracleAS TopLink Mapping Workbench Export Project to Java Source... menu command to create a starting point for coding the project class manually.


Example 3-45 illustrates how you can specify OracleAS TopLink projects in code.

Example 3-45 Specifying an OracleAS TopLink Project in Code

/**
* The class EmployeeProject is an example of an OracleAS TopLink project defined 
in Java code. The individual parts of the project - the Login and the 
descriptors, are built inside of methods that are called by the constructor. 
Note that EmployeeProject extends the class oracle.toplink.sessions.Project.
*/
public class EmployeeProject extends oracle.toplink.sessions.Project{

/**
* Supply a zero argument constructor that initializes all aspects of the 
project. Make sure that the login and all the descriptors are initialized and 
added to the project.
*/
public EmployeeProject(){
applyPROJECT();
applyLOGIN();
buildAddressDescriptor();
buildEmployeeDescriptor();
// other methods to build all descriptors for the project
/**
* Project-level properties, such as the name of the project, should be specified 
here.
*/
protected void applyPROJECT(){
setName("Employee");
}
protected void applyLOGIN()
{
    oracle.toplink.sessions.DatabaseLogin login = 
      new oracle.toplink.sessions.DatabaseLogin();

    // use platform appropriate for underlying database
    login.setPlatformClassName( "oracle.toplink.internal.databaseaccess.
      OraclePlatform");

    // if no sequencing is used, setLogin() will suffice
    setLoginAndApplySequenceProperties(login);
}

/**
* Descriptors are built by defining table info, setting properties (caching, 
etc.) and by adding mappings to the descriptor.
*/
protected void buildEmployeeDescriptor() {
    oracle.toplink.publicinterface.Descriptor descriptor = 
      new oracle.toplink.publicinterface.Descriptor();
}

// SECTION: DESCRIPTOR
// specify the class to be made persistent
descriptor.setJavaClass(examples.ejb.cmp11.advanced.EmployeeBean.class);

// specify the tables to be used and primary key
Vector tables = new Vector();
tables.addElement("EJB_EMPLOYEE");
descriptor.setTableNames(tables);
descriptor.addPrimaryKeyFieldName("EJB_EMPLOYEE.EMP_ID");

// SECTION: PROPERTIES
descriptor.setIdentityMapClass(
oracle.toplink.internal.identitymaps. FullIdentityMap.class);
descriptor.setExistenceChecking("Check cache");
descriptor.setIdentityMapSize(100);

// SECTION: COPY POLICY
descriptor.createCopyPolicy("constructor");

// SECTION: INSTANTIATION POLICY
descriptor.createInstantiationPolicy("constructor");

// SECTION: DIRECTTOFIELDMAPPING
oracle.toplink.mappings.DirectToFieldMapping firstNameMapping =
  new oracle.toplink.mappings .DirectToFieldMapping();
firstNameMapping.setAttributeName("firstName");
firstNameMapping.setIsReadOnly(false);
firstNameMapping.setFieldName("EJB_EMPLOYEE.F_NAME");
descriptor.addMapping(firstNameMapping);

// ... Additional mappings are added to the descriptor using the addMapping() 
method.
},}

To deploy the OracleAS TopLink project, specify the project class name in the project-class element in the toplink-ejb-jar.xml file for your entity beans.

For example:

<session>
    <name>EmployeeDemo</name>
    <project-class>oracle.toplink.demos.ejb.cmp.wls.employee.EmployeeProject
    </project-class>
    <login>
        <connection-pool>ejbPool</connection-pool>
    </login>
</session>

Writing Mappings in Code

In most cases, the OracleAS TopLink Mapping Workbench is the preferred tool to create OracleAS TopLink elements however, OracleAS TopLink also supports building components of your application in Java code. You can code components ranging in size from small elements to complete projects. This section illustrates the techniques required for building several of these components, and includes discussions on:

Implementing Object-Relational Descriptors in Java

Use the ObjectRelationalDescriptor class to define object-relational descriptors. This descriptor subclass contains the following additional properties:

The OracleAS TopLink Remote (RMI) Example illustrates an object-relational data model and descriptors. For more information, see the OracleAS TopLink Examples at <ORACLE_HOME>\toplink\doc\examples.htm.

Example 3-46 Creating an Object-Relational Descriptor

import oracle.toplink.objectrelational.*;
ObjectRelationalDescriptor descriptor = new ObjectRelationalDescriptor()
descriptor.setJavaClass(Employee.class);
descriptor.setTableName("EMPLOYEES");
descriptor.setStructureName("EMPLOYEE_T");
descriptor.setPrimaryKeyFieldName("OBJECT_ID");

descriptor.addFieldOrdering("OBJECT_ID"); 
descriptor.addFieldOrdering("F_NAME");
descriptor.addFieldOrdering("L_NAME");
descriptor.addFieldOrdering("ADDRESS"); 
descriptor.addFieldOrdering("MANAGER");
descriptor.addDirectMapping("id", "OBJECT_ID"); 
descriptor.addDirectMapping("firstName", "F_NAME"); 
descriptor.addDirectMapping("lastName", "L_NAME");
//Refer to the mappings section for examples of object relational mappings.
...

Implementing Primary Keys in Java

If a single field constitutes the primary key, send the setPrimaryKeyFieldName() message to the descriptor. For a composite primary key, send the addPrimaryKeyFieldName() message for each field that makes up the primary key.

Alternatively, use the setPrimaryKeyFieldNames( ) message that sends a Vector of the fields used as the primary key.

Example 3-47 Setting a Single-Field Primary Key in Java

// Define a new descriptor and set the primary key.
descriptor.setPrimaryKeyFieldName("ADDRESS_ID");

Example 3-48 Setting a Composite Primary Key in Java

// Define a new descriptor and set the primary key.
descriptor1.addPrimaryKeyFieldName("PHONE_NUMBER");
descriptor1.addPrimaryKeyFieldName("AREA_CODE");

Implementing Inheritance in Java

Although you can implement inheritance hierarchy in Java, under most circumstances, we recommend you use the OracleAS TopLink Mapping Workbench.

To implement an inheritance hierarchy completely in Java, modify the descriptors for the superclass and its subclasses. The inheritance implementation for a descriptor is encapsulated in an InheritancePolicy object, which is accessed by sending getInheritancePolicy() to the descriptor:

Queries for Inherited Superclasses and Multiple Tables

If a superclass is configured to read subclasses and its subclasses define additional tables, build multiple queries to obtain all the rows for all the subclasses. For best performance in this situation, create a view against which to execute the query using the setReadAllSubclassesViewName() method. The view must internally perform an outer join or union on all the subclass tables and return a single result set with all the data.

Customizing Inheritance

Occasionally, using the default OracleAS TopLink inheritance mechanism is not possible. For these cases, you can customize the inheritance mechanism. Instead of using a class indicator field and mapping, use a class extraction method. This method takes the object's row and returns the class to be used for that row. The setClassExtractionMethodName() method is used to accomplish this.

Queries for inherited classes usually also require filtering of the table rows. By default, OracleAS TopLink generates this from the class indicator information. If you provide the class extraction method, specify the filtering expressions. These can be set for concrete classes through setOnlyInstancesExpression() and for branch classes through setWithAllSubclassesExpression().

Figure 3-10 illustrates an example of an inheritance hierarchy. The Vehicle-Bicycle branch demonstrates how you can store all subclass information in one table. The FueledVehicle-Car branch demonstrates how you can store subclass information in two tables.

Figure 3-10 Inheritance Hierarchy

Text description of inhrtnc.gif follows

Text description of the illustration inhrtnc.gif

The Car and Bicycle classes are leaf classes. Queries performed on them return instances of Car and Bicycle respectively.

FueledVehicle is a branch class. By default, branch classes are configured to read instances and subclass instances. Queries for FueledVehicle return instances of FueledVehicle and instances of Car.

NonFueledVehicle is a branch class and is configured to read subclasses. Because it does not have a class indicator defined in the root, it cannot be written to the database. Queries performed on NonFueledVehicle return instances of its subclasses.

Vehicle is a root class, which is configured to read instances of itself and instances of its subclass by default. Queries performed on the Vehicle class return instances of any of the concrete classes in the hierarchy.

Example 3-49 Implementing Descriptors for the Classes in the Inheritance Hierarchy

// Vehicle is a root class. Because it is the root class, it must add the class 
indicators for its subclasses.
public static Descriptor descriptor()
{
Descriptor descriptor = new Descriptor();
descriptor.setJavaClass(Vehicle.class);
descriptor.setTableName("VEHICLE");
descriptor.setPrimaryKeyFieldName("ID");

// Class indicators must be supplied for each of the subclasses in the hierarchy 
that can have instances.
InheritancePolicy policy = descriptor.getInheritancePolicy();
policy.setClassIndicatorFieldName("TYPE");
policy.addClassIndicator(FueledVehicle.class, "Fueled");
policy.addClassIndicator(Car.class, "Car");
policy.addClassIndicator(Bicycle.class, "Bicycle");

descriptor.addDirectMapping("id", "ID");
descriptor.addDirectMapping("passengerCapacity", "CAP");

return descriptor;
}

// FueledVehicle descriptor; it is a branch class and a subclass of Vehicle. 
Queries made on this class will return instances of itself and instances of its 
subclasses.
public static Descriptor descriptor()
{
Descriptor descriptor = new Descriptor();
descriptor.setJavaClass(FueledVehicle.class);
descriptor.addTableName("FUEL_VEH");
descriptor.getInheritancePolicy().setParentClass(Vehicle.class);
descriptor.addDirectMapping("fuelCapacity", "FUEL_CAP");
descriptor.addDirectMapping("fuelType", "FUEL_TYPE");
return descriptor;
}

// Car descriptor; it is a leaf class and subclass of FueledVehicle.
public static Descriptor descriptor()
{
Descriptor descriptor = new Descriptor();
descriptor.setJavaClass(Car.class);
descriptor.addTableName("CAR");
descriptor.getInheritancePolicy().setParentClass(FueledVehicle.class);

// Next define the attribute mappings.
descriptor.addDirectMapping("description", "DESCRIP");
descriptor.addDirectMapping("fuelType", "FUEL_VEH.FUEL_TYPE");
return descriptor;
}

// NonFueledVehicle descriptor; it is a branch class and a subclass of Vehicle. 
Queries made on this class will return instances of its subclasses.
public static Descriptor descriptor()
{
Descriptor descriptor = new Descriptor();
descriptor.setJavaClass(NonFueledVehicle.class);
descriptor.getInheritancePolicy().setParentClass(Vehicle.class);
return descriptor;
}

// Bicycle descriptor; it is a leaf class and subclass of NonFueledVehicle. 
public static Descriptor descriptor()
{
Descriptor descriptor = new Descriptor();
descriptor.setJavaClass(Bicycle.class);
descriptor.getInheritancePolicy().setParentClass(NonFueledVehicle.class);
descriptor.addDirectMapping("description", "BICY_DES");
return descriptor;
}

// FueledVehicle class; If a class extraction method is used, the following 
needs to be added to specify that only the branch class itself needs to be 
returned. This example is just specifying the class indicator field, which can 
also be specified in the OracleAS TopLink Mapping Workbench in the Descriptor 
Advanced Properties dialog.
public void addToDescriptor(Descriptor descriptor) 
{
ExpressionBuilder builder = new ExpressionBuilder();
descriptor.getInheritancePolicy().setOnlyInstancesExpression(builder.getField
  ("VEHICLE.TYPE").equal("F"));
}

Reference

Table 3-7 summarizes the most common public methods for InheritancePolicy. For more information about the available methods for InheritancePolicy, see the Oracle Application Server TopLink API Reference.

Table 3-7 Elements for the Inheritance Policy  
Element Default Method Name

Class indicators

use indicator mapping

setClassIndicatorFieldName(String fieldName)

Parent classes

not applicable

setParentClass(Class parentClass)

Implementing Indirection in Java

To create indirection objects in code, the application must replace the relationship reference with a ValueHolderInterface. It must also call the useIndirection() method of the mapping if the mapping does not use indirection by default. Likewise, call the dontUseIndirection() method to disable indirection. ValueHolderInterface is defined in the oracle.toplink.indirection.

Example 3-50 A Mapping that Does Not Use Indirection

// Define the One-to-One mapping. Note that One-to-One mappings have indirection 
enabled by default, so the "dontUseIndirection()" method must be called if 
indirection is not used.
OneToOneMapping oneToOneMapping = new OneToOneMapping();
oneToOneMapping.setAttributeName("address");
oneToOneMapping.setReferenceClass(Address.class);
oneToOneMapping.setForeignKeyFieldName("ADDRESS_ID");
oneToOneMapping.dontUseIndirection();
oneToOneMapping.setSetMethodName("setAddress");
oneToOneMapping.setGetMethodName("getAddress");
descriptor.addMapping(oneToOneMapping);

The following code illustrates a mapping using indirection.

// Define the One-to-One mapping. One-to-One mappings have indirection enabled 
by default, so the "useIndirection()" method is unnecessary if indirection is 
used.
OneToOneMapping oneToOneMapping = new OneToOneMapping();
oneToOneMapping.setAttributeName("address");
oneToOneMapping.setReferenceClass(Address.class);
oneToOneMapping.setForeignKeyFieldName("ADDRESS_ID");
oneToOneMapping.setSetMethodName("setAddressHolder");
oneToOneMapping.setGetMethodName("getAddressHolder");
descriptor.addMapping(oneToOneMapping);

Implementing Interfaces in Java

Descriptors can own their parent interfaces. They can set multiple interfaces if they have implemented multiple interfaces. The query keys are defined in a normal way except that they must define the abstract query key from the interface descriptor in their descriptors. An abstract query key on the interface descriptor enables it to write expression queries on the interface.

Example 3-51 Using an Abstract Query Key on the Interface Descriptor

ExpressionBuilder contact = new ExpressionBuilder();
session.readObject(Contact.class, contact.get("id").equal(2));

Setting the Copy Policy in Java

The Descriptor class provides three methods that determine how an object is cloned:

Implementing Multiple Tables in Java

To define a multiple table descriptor, call the addTableName() method for each table the descriptor maps to. If the descriptor inherits its primary table and is defining only a single additional one, then the descriptor is mapped normally to this table.

Primary Keys Match

Normally, the primary key is defined only for the primary table of the descriptor. The primary table is the first table specified through addTableName(). The primary key is not defined for the additional tables and is required to be the same as in the primary table. If the additional table's key is different, refer to the next example.

By default, all the fields in a mapping are presumed to be part of the primary table. If a mapping's field is for one of the additional tables, it must be fully qualified with the field's table name.

Example 3-52 Implementing a Multiple Table Descriptor In Which the Primary Keys Match

//Define a new descriptor that uses three tables.
Descriptor descriptor = new Descriptor();
descriptor.setJavaClass(Employee.class);
descriptor.addTableName("PERSONNEL"); // Primary table
descriptor.addTableName("EMPLOYMENT");
descriptor.addTableName("USERS");

descriptor.addPrimaryKeyFieldName("PER_NUMBER");
descriptor.addPrimaryKeyFieldName("DEP_NUMBER");

descriptor.addDirectMapping("id", "PER_NUMBER");
descriptor.addDirectMapping("firstName", "F_NAME");
descriptor.addDirectMapping("lastName", "L_NAME");

OneToOneMapping department = new OneToOneMapping();
department.setAttributeName("department");
department.setReferenceClass(Department.class);
department.setForeignKeyFieldName("DEP_NUMBER");
descriptor.addMapping(department);
// Mapping the primary key fields in the additional tables is not required
descriptor.addDirectMapping("salary", "EMPLOYMENT.SALARY");

AggregateObjectMapping period = new AggregateObjectMapping();
period.setAttributeName(period);
period.setReferenceClass(EmployementPeriod.class);
period.addFieldNameTranslation("EMPLOYMENT.S_DATE", "S_DATE");
period.addFieldNameTranslation("EMPLOYMENT.E_DATE", "E_DATE");
descriptor.addMapping(period);

descriptor.addDirectMapping("userName", "USERS.NAME");
descriptor.addDirectMapping("password", "USERS.PASSWORD");

Primary Keys are Named Differently

If the additional table's primary key is named differently, then call the descriptor method addMultipleTablePrimaryKeyName(), which provides:

Example 3-53 Implementing a Multiple Table Descriptor In Which the Additional Table Primary Keys are Named Differently

//Define a new descriptor that uses three tables.
Descriptor descriptor = new Descriptor();
descriptor.setJavaClass(Employee.class);
descriptor.addTableName("PERSONNEL"); 
// Primary table
descriptor.addTableName("EMPLOYMENT");
descriptor.addTableName("USERS");

descriptor.addPrimaryKeyFieldName("PER_NUMBER");
descriptor.addPrimaryKeyFieldName("DEP_NUMBER");

descriptor.addMultipleTablePrimaryKeyName("PERSONEL.PER_NUMBER",
  "USERS.PERSONEL_NO");
descriptor.addMultipleTablePrimaryKeyName("PERSONEL.DEP_NUMBER",
  "USERS.DEPARTMENT_NO");

// Assumed EMPLOYMENT uses same primary key
descriptor.addDirectMapping(id, PER_NUMBER);

OneToOneMapping department = new OneToOneMapping();
department.setAttributeName("department");
department.setReferenceClass(Department.class);
department.setForeignKeyFieldName("DEP_NUMBER");
descriptor.addMapping(department);

// Primary key does not have to be mapped for additional tables.
...

Tables Related by Foreign Key Relationships

For OracleAS TopLink to support read, insert, update, and delete operations on an object mapped to multiple tables:

The API is addMultipleTableForeignKeyFieldName(). This method builds the join expression and adjusts the table insertion order to respect the foreign key constraints.

Example 3-54 illustrates the setup of a descriptor for an object mapped to multiple tables in which the tables are related by a foreign key relationship from the primary table to the secondary table. The addMultipleTableForeignKeyFieldName() method is used to specify the direction of the foreign key relationship.

If the foreign key is in the secondary table and refers to the primary table, then the order of the arguments to addMultipleTableForeignKeyFieldName() is reversed.


Note:

To allow read, insert, update, and delete operation to be performed on the Employee object, map the foreign key field in the primary table and the primary key in the secondary table.


Example 3-54 Implementing Multiple Tables In Which a Foreign Key from the Primary Table to the Secondary Table is Used to Join the Tables

Descriptor descriptor = new Descriptor();
descriptor.setJavaClass(Employee.class);
Vector vector = new Vector();
vector.addElement("EMPLOYEE");
vector.addElement(ADDRESS");
descriptor.setTableNames(vector);
descriptor.addPrimaryKeyFieldName("EMPLOYEE.EMP_ID");
// Map the foreign key field of the employee table and the primary key of the 
address table.
descriptor.addDirectMapping("addressID", "EMPLOYEE.ADDR_ID");

// Setup the join from the address table to the country employee table to the 
address table by specifying the FK info to the descriptor. Set the foreign key 
info from the address table to the country table. 
descriptor.addMultipleTableForeignKeyFieldName("EMPLOYEE.ADDR_ID",
  "ADDRESS.ADDR_ID");

Non Standard Table Relationships

Occasionally the join condition can be nonstandard. In this case, the descriptor's query manager can be used to provide a custom multiple table join expression. The getQueryManager() method is called on the descriptor to obtain its query manager, and the setMultipleTableJoinExpression() method is used to customize the join expression.

Simply specifying the join expression allows OracleAS TopLink to perform read operations for the object. Insert operations can also be supported if the table insertion order is specified and the primary key of the additional tables is mapped manually.

The insertion order is required to conform to foreign key constraints when inserting to the multiple tables. Specify the insert order using the descriptor method setMultipleTableInsertOrder().

Example 3-55 illustrates the use of the setMultipleTableJoinExpression() and setMultipleTableInsertOrder() methods. In addition, it illustrates the use of a custom join expression without specifying the table insert order.


Note:

Using these methods does not support update or delete operations because of the lack of primary key information for the secondary table(s). If update and delete operations are required, perform them with custom SQL, or explicitly specify the foreign key information as explained in the previous section.


Example 3-55 Implementing Multiple Tables In Which You Specify a Join Expression and the Table Insert Order

Using this method allows only read and insert operations to be performed on Employee objects. Note that the primary key of the secondary table, and the foreign key of the primary table must be mapped and maintained by the application for insert operations to work.

Descriptor descriptor = new Descriptor();
descriptor.setJavaClass(Employee.class);
Vector vector = new Vector();
vector.addElement("EMPLOYEE");
vector.addElement(ADDRESS");
descriptor.setTableNames(vector);

// Specify the primary key information for each table.
descriptor.addPrimaryKeyFieldName("EMPLOYEE.EMP_ID");

// Map the foreign key field of the employee table and the primary key of the 
address table. 
descriptor.addDirectMapping("employee_addressID", "EMPLOYEE.ADDR_ID");
descriptor.addDirectMapping("address_addressID", "ADDRESS.ADDR_ID");
// Setup the join from the employee table to the address table using a custom 
join expression and specifying the table insert order.
ExpressionBuilder builder = new ExpressionBuilder();
descriptor.getQueryManager().setMultipleTableJoinExpression(builder.getField
  ("EMPLOYEE.ADDR_ID").equal(builder.getField("ADDRESS.ADDR_ID")));
Vector tables = new Vector(2);
tables.addElement(new DatabaseTable("ADDRESS"));
tables.addElement(new DatabaseTable("EMPLOYEE"));
descriptor.setMultipleTableInsertOrder(tables);
...

Example 3-56 Mapping a Multiple Table Descriptor In Which a Custom Join Expression is Required

In this example, only read operations are supported.

//Define a new descriptor that uses three tables.
Descriptor descriptor = new Descriptor();
descriptor.setJavaClass(Employee.class);
descriptor.addTableName("PERSONNEL"); 
// Primary table
descriptor.addTableName("EMPLOYMENT");
descriptor.addPrimaryKeyFieldName("PER_NO");
descriptor.addPrimaryKeyFieldName("DEP_NO");

ExpressionBuilder builder = new ExpressionBuilder();
descriptor.getQueryManager().setMultipleTableJoinExpression((builder.getField
  ("PERSONEL.EMP_NO").equal(builder.getField("EMPLOYMENT.EMP_NO")));
descriptor.addDirectMapping("personelNumber", "PER_NO");

OneToOneMapping department = new OneToOneMapping();
department.setAttributeName("department");
department.setReferenceClass(Department.class);
department.setForeignKeyFieldName("DEP_NO");
descriptor.addMapping(department);
// The primary key field on the EMPLOYMENT does not have to be mapped.
...

Implementing Sequence Numbers in Java

To implement sequence numbers using Java code, send the setSequenceNumberFieldName( ) message to the descriptor to register the name of the database field that holds the sequence number. The setSequenceNumberName( ) method also holds the name of the sequence. This name can be one of the entries in the SEQ_NAME column or the name of the sequence object (if you are using Oracle native sequencing).


Notes:

  • The sequence field must be in the first (primary) table if multiple tables are used.

  • If you use Sybase, Microsoft SQL Server, or IBM Informix native sequencing, this implementation has no direct meaning but must still be set for compatibility reasons.


Implementing Locking in Java

Use the API to set optimistic locking completely in code. All the API is on the descriptor:

Example 3-57 Implementing Optimistic Locking Using the Version Field of Employee Table as the Version Number of the Optimistic Lock

// Set the field that control optimistic locking. No mappings are set for fields 
which are version fields for optimistic locking.
descriptor.useVersionLocking("VERSION");

The code in Example 3-57 stores the optimistic locking value in the identity map. If the value must be stored in a nonread only mapping, then the code can be:

descriptor.useVersionLocking("VERSION", false);

The false indicates that the lock value is not stored in the cache, but is stored in the object.

Java Implementation of Optimistic Locking

Use the API to set optimistic locking in code. All the API is on the descriptor:

Example 3-58 illustrates how to implement optimistic locking using the VERSION field of EMPLOYEE table as the version number of the optimistic lock.

Example 3-58 Implementing Optimistic Locking Example

descriptor.useVersionLocking("VERSION");

The code in Example 3-58 stores the optimistic locking value in the identity map. If the value must be stored in a nonread only mapping, then the code appears as follows:

descriptor.useVersionLocking("VERSION", false);

The false indicates that the lock value is not stored in the cache, but is stored in the object.


Go to previous page Go to next page
Oracle
Copyright © 2000, 2003 Oracle Corporation.

All Rights Reserved.
Go To Documentation Library
Home
Go To Product List
Solution Area
Go To Table Of Contents
Contents
Go To Index
Index