Skip Headers
Oracle® Application Server TopLink Application Developer's Guide
10g Release 2 (10.1.2)
Part No. B15901-01
  Go To Documentation Library
Home
Go To Product List
Solution Area
Go To Table Of Contents
Contents
Go To Index
Index

Previous
Previous
Next
Next
 

Basic Mappings

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 OracleAS TopLink Mapping Workbench or the Java code-based API. However, Oracle recommends 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

Description of metamap.gif follows
Description of the illustration metamap.gif

For more information about 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 attribute being mapped, set by sending the setAttributeName() message

  • The field to store the value of the attribute, set by sending the setFieldName() message

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:

  • Map one attribute to one field

  • Map more than one attribute to one field (to create different views of the same field)

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 in which 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.

Note the following example: First you want to map the class that appears in Example 3-4 to create two different views of the same, underlying database field. Then 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 must 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 the OracleAS TopLink method of applying the data type of the last writable mapping, which is integer in in this example.

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

  • Disable data conversion optimization with DatabaseLogin.setShouldOptimizeDataConversion(false).

  • Map the attribute with the highest precision as writable.

To change your design so that 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 require the following elements:

  • The attribute mapped, set by sending the setAttributeName() message

  • The field to store the value of the attribute, set by the setFieldName() message

  • The Java type stored in the attribute, set by sending the setAttributeClassification() message

  • The database type to be written, set by sending the setFieldClassification() message

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 attribute mapped, set by sending the setAttributeName() message

  • The field to store the value of the attribute, set by the setFieldName() message

  • A set of values and their conversions, added by sending the addConversionValue() message

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

  • addToAttributeOnlyConversionValue(Object fieldValue, Object attributeValue): This is a one-way mapping from the field to the attribute. Use this mapping if multiple database values map to the same object value. When written to the database, the value entered by addConversionValue(Object fieldValue, Object attributeValue) is used, and the original values in the database change.

  • setDefaultAttributeValue Object defaultAttributeValue): Substitutes the default value for any unmapped value retrieved from the database. When writing to the database, the value entered by addConversionValue(Object fieldValue, Object attributeValue) is used, and the original values in the database change.

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, then you must load only the bean classes into OracleAS TopLink Mapping Workbench—not the Remote, Local, or Home interfaces. When you define a relationship mapping in both 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 persistence layer manages bean relationships, and the relationships do not require any internal use of finder methods.

  • You can define one-to-one, one-to-many, and many-to-many relationships between beans.

  • You can use dependent objects (regular Java objects) to model fine-grained objects that are associated with a particular entity.

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:

  • CMP beans must be abstract and have only virtual fields.

  • Collections of entities used in relationship mappings must not be implemented by the bean developer, and must never be exposed directly to the client.

  • Beans referenced by other beans must be related through Local interfaces.

  • The EJB 2.0 specification does not support method access (such as get and set methods) for mappings.

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 OracleAS TopLink Mapping Workbench

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 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, privately owned mappings (bean is source, Java object is target)

  • One-to-many, privately owned mappings (bean is source, Java objects are target)

  • Aggregate mappings (bean is source, Java object is target)

  • Direct collection mappings (bean is source, Java object is target and is a base data type, such as String or Date)


    Notes:

    Relationships from entity beans to regular Java objects must be dependent. If you expose dependent objects to the client, these objects must be serializable.

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, in which, 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:

  • The attribute mapped, set by sending the setAttributeName() message

  • The reference class, set by sending the setReferenceClass() message

  • The foreign key information, normally specified by sending the setForeignKeyFieldName() message and passing the foreign key field from the source table that references the primary key of the target table


    Note:

    If the target primary key is composite, send the addForeignKeyFieldName() message for each of the foreign fields and target primary key fields that make up the relationship.

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:

  • One mapping must send the setForeignKeyFieldName() message.

  • The other mapping must send the setTargetForeignKeyFieldName() message.

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 "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 tables 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:

  • The attribute mapped, set by sending the setAttributeName() message

  • The target (child) class, set by sending the setReferenceClass() message

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

  • Send the descriptorIsAggregate() message to the descriptor to indicate that all information must come from the rows of its parent object's rows

  • Include no table or primary key information for the target class

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:

  • The attribute being mapped, set by sending the setAttributeName() message

  • The reference class, set by sending the setReferenceClass() message

  • The foreign key information, which you specify by sending the setTargetForeignKeyFieldName() message and passing a field in the target object's associated table that refers to the primary key in the owning object's table


    Note:

    If the target primary key is composite, send the addTargetForeignKeyFieldName() message for each of the fields that make up the key.

  • A one-to-one mapping in the target class back to the source class

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


    Note:

    Because indirection is enabled by default for a one-to-many mapping, the attribute must implement ValueHolderInterface.

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 illustrated in Example 3-14, other common API for use to implement indirection in aggregate collection include:

  • useBasicIndirection(): implements OracleAS TopLink valueholder indirection.

  • useTransparentCollection(): if you use transparent indirection, this element places a special collection in the source object's attribute.

  • dontUseIndirection(): implements no indirection.

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, no back reference is 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:

  • The descriptor of the target class must declare itself as an aggregate collection object. Unlike the aggregate object mapping, in which the target descriptor does not have a specific table to associate with, there must be a target table for the target object.

  • The descriptor of the source class must add an aggregate collection mapping that specifies the target class.

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:

  • The attribute mapped, set by sending the setAttributeName() message

  • The reference class, set by sending the setReferenceClass() message

  • The foreign key information, specified by sending the addTargetForeignKeyFieldName() message and passing the field name of the target foreign key and the source of the primary key in the source table


    Notes:

    If the source primary key is composite, send the addTargetForeignKeyFieldName() message to each of the fields that make up the key. Because indirection is enabled by default for an aggregate collection mapping, the attribute must implement ValueHolderInterface.

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 illustrated in Example 3-15, other common API for use to implement indirection in aggregate collection mappings include:

  • useBasicIndirection(): implements OracleAS TopLink valueholder indirection.

  • useTransparentCollection(): If you use transparent indirection, this element places a special collection in the source object's attribute.

  • dontUseIndirection(): implements no indirection.

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:

  • The attribute mapped, set by sending the setAttributeName() message

  • The database table that holds the values to be stored in the collection, set by sending the setReferenceTableName() message

  • The field in the reference table from which the values are read and placed into the collection; this is called the direct field and is set by sending the setDirectFieldName() message

  • The foreign key information, which you specify by sending the setReferenceKeyFieldName() message and passing the name of the field that is a foreign reference to the primary key of the source object


    Note:

    If the target primary key is composite, send the addReferenceKeyFieldName() message for each of the fields that make up the key.

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 illustrated in Example 3-16, other common API for use with direct collection mappings include:

  • useBasicIndirection(): implements OracleAS TopLink valueholder indirection.

  • useTransparentCollection(): If you use transparent indirection, this element places a special collection in the attribute of the source object.

  • dontUseIndirection(): implements no indirection.

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 require the following elements:

  • The attribute mapped, set by sending the setAttributeName() message

  • The reference class, set by sending the setReferenceClass() message

  • The relation table, set by sending the setRelationTableName() message

  • The foreign key information (for noncomposite target primary keys), which you specify by sending the setSourceRelationKeyFieldName() and setTargetRelationKeyFieldName() messages

  • The foreign key information if the source or target primary keys are composite, which you specify by sending the addSourceRelationKeyFieldName() or addTargetRelationKeyFieldName() messages

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 illustrated in Example 3-17, other common API for use with many-to-many mappings include:

  • useBasicIndirection(): implements OracleAS TopLink valueholder indirection.

  • useTransparentCollection(): If you use transparent indirection, this element places a special collection in the attribute of the source object.

  • dontUseIndirection(): implements no indirection.

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:

  • Valueholder indirection: places a special OracleAS TopLink object with an interface between the pair of related objects.

  • Proxy indirection: uses a dynamically constructed object with the same interface as the class of the object referenced in the relationship.

  • Transparent indirection: a special OracleAS TopLink collection that prevents instantiation of the objects it contains until they are called. The collections conform to Vector, Hashtable, or Collection interfaces.

Indirection represents an effective way to improve the efficiency of your application, and we recommend you implement it 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—if you do this, remember to 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:

  • One-to-one mapping

  • Variable one-to-one mapping

  • Reference mapping

  • Transformation mapping

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 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:

  • The target class of the one-to-one relationship must implement a defined public interface.

  • The one-to-one attribute on the source class must be of the interface type defined in the target class.

  • If you employ method accessing, the get() and set() methods must use the interface.

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;

    //Step 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() {
    ...
    }
}

//Step 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 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 help 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:

  • Your application can tolerate the addition of OracleAS TopLink classes to your model.

  • The relationship to which you are applying indirection involves EJB 2.0 entity beans.

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 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 use of indirection objects

  • Transparent indirection

  • Proxy indirection

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

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

  • Uninstantiated valueholders (indirection objects) do not survive serialization. If you send a valueholder from the server to the client, it will no longer function unless it has been previously triggered.

  • You can use valueholders in bean-to-bean relationships and bean-to-object relationships, but avoid them in relationships in which the source is likely to be serialized to the client.

  • Do not serialize collections that use untriggered transparent indirection to the client application. These collections do not function if they are serialized.

  • Proxy indirection is unavailable for relationships whose target is an entity bean. The proxies used for this kind of indirection interfere with the RMI stubs and skeletons generated for the entity. If proxies exist, instantiate them before serializing to the client.

  • Use valueholders for bean-to-bean relationships and for bean-to-object relationships. You can also use transparent indirection for collections that are not exposed to the client application.

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:

  • Transparent indirection for one-to-many or many-to-many relationships

  • Valueholder indirection for one-to-one relationships

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:

  • Add triggering methods directly on the object.

  • Build helper classes to trigger valueholders, and use a method on the serializable object to call the helper classes.

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:

  • Trigger no Valueholders: This method requires no extra work; however, it assumes that the application that receives the serializable object does not call any valueholders, including those in the serialized object.

  • Trigger a set of Valueholders specific to the purpose of the receiving application: This method requires the OracleAS TopLink developer to know exactly what the receiving application does with the serialized object and to manually trigger all required valueholders.

  • Trigger a set of Valueholders to make the serialized object generically useful: For example, you can choose to trigger all valueholders in the object itself, but none in the related objects. This method does not require that the OracleAS TopLink developer know what the receiving application does with the serialized objects, but imposes a predictable limit on the receiving application.

  • Trigger all Valueholders, traversing all relationships to the leaf class: This method makes the object completely fail-safe to the receiving application, but imposes the potentially resource-intensive overhead associated with triggering all objects in the relationship hierarchy on the OracleAS TopLink application.

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:

  • Merge only the direct attributes of the object being read: Use the shallowMergeClone(java.lang.Object rmiClone) method to capture changes in the deserialized object only. Use this option when you know that changes to the object do not extend to related objects.

  • Merge the deserialized object and its privately owned parts: Use the mergeClone(java.lang.Object rmiClone) method to capture changes in the deserialized object and any of its privately owned objects.

  • Merge the deserialized object and all referenced objects: Use the mergeCloneWithReferences(java.lang.Object rmiClone) method to capture changes to the deserialized object, its privately owned objects, and all its referenced objects. Note that the referenced objects include only those objects with a direct relationship to the deserialized object.

  • Merge the entire relationship graph of the deserialized object: Use the deepMergeClone(java.lang.Object rmiClone) method to capture all changes to the relationship graph of the deserialized object. This method causes OracleAS TopLink to traverse all relationships from the deserialized object to its leaf objects and merge any changes it finds.

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, then OracleAS TopLink merges those objects into the model if one of the following conditions is met:

  • The new objects do not exist in the OracleAS TopLink model.

  • The new objects exist in the OracleAS TopLink model and are registered with the Unit of Work.

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:

  • If a table uses a combination of columns to create a primary key (a composite primary key), declare all the necessary fields as primary keys.

  • Sequencing is the most common method to implement a primary key.

  • Descriptors must always provide mappings for a primary key. These mappings can be direct, transformation, or one-to-one.

  • You do not have to define a primary key constraint in the database, but you must ensure that the fields you specify for the primary key are unique.

Under most circumstances, you set primary key information in 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 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 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:

  • The name of the table that stores the primary key for the class

  • The name of the column in the table that stores the primary key for each object (the sequencing column)

Figure 3-3 Sequencing Elements in a Class Table

Description of seqtable.gif follows
Description of the illustration seqtable.gif

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 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 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:

  • SEQ_NAME, which specifies the class type to which the selected row refers

  • SEQ_COUNT, which specifies the highest sequence number currently allocated for the object represented in the selected row

Figure 3-4 OracleAS TopLink SEQUENCE Table

Description of seqtblmn.gif follows
Description of the illustration seqtblmn.gif

The rows of the SEQUENCE table represent every class that participates in sequencing. When you configure sequencing in 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:

  • You want to use an existing sequence table for sequencing.

  • You do not want to use the default naming convention for the table and its columns.

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:

  • Each style of racquet has its own class table.

  • Each manufactured racquet is an object, represented by a line in the class table.

  • The system assigns serial numbers that use sequencing to the racquets.

Figure 3-5 Example of Database Tables—Racquet Information

Description of orseqa.gif follows
Description of the illustration orseqa.gif

The manufacturer can:

  • Use separate sequencing for each racquet style. The DBA builds three separate SEQUENCE objects, perhaps called ATTACK_SEQ, VOLLEY_SEQ, and PROX_SEQ. Each different racquet line has its own serial number series, and there may be duplication of serial numbers between the lines (for example, all three styles may include a racquet with serial number 1234).

  • Use a single sequencing series for all rackets. The DBA builds a single SEQUENCE object (perhaps called RACQUET_SEQ). The manufacturer assigns serial numbers to racquets as they are produced, without regard for the style of racquet.


    Note:

    If the manufacturer chooses this second option, he may also choose to combine the three tables into a single table to improve database efficiency.

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:

  • For Sybase SQL Server and Microsoft SQL Server databases, set the primary key field to the type IDENTITY.

  • For IBM Informix databases, set the primary key field to the type SERIAL.


    Note:

    OracleAS TopLink does not support native sequencing in IBM DB2 databases.

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 you use native sequencing as offered in Sybase SQL Server, Microsoft SQL Server, or IBM Informix databases, be aware that:

  • Native sequencing does not strictly conform to any EJB specification, because it does not initialize the primary key for a created object until you commit the transaction that creates the object. EJB specifications expect that the primary key is available at ejbPostCreate() time.

  • OracleAS TopLink CMP integration for IBM WebSphere does not support native sequencing other than Oracle native sequencing.

  • BEA WebLogic supports native sequencing; however, this type of native sequencing does not assign or return a primary key for a created object until you commit the transaction in which the object is created. Because of this, if you use native sequencing, commit a transaction immediately after calling the ejbCreate method to avoid problems with object identity in the OracleAS TopLink cache and the container.

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 it 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:

  • Foreign key: key added to the table associated with the mapping's own descriptor.

  • Target foreign key: key that references the target object's table back to the key from the mapping descriptor's table. The key in the mapping descriptor table is a foreign key in the target table that the target table uses to reference the mapping descriptor's table.

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 have a subclass with a superclass mapped to one table, and the subclass has additional attributes that are also mapped to a second table.

  • A class is not involved in inheritance, and its data is spread out across multiple tables.

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.

See "Implementing Multiple Tables in Java" for more information about implementing multiple table mappings in code.

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 OracleAS TopLink Mapping Workbench

If you use OracleAS TopLink Mapping Workbench to build projects with entity beans, you can load the bean classes themselves into 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 classpath used by OracleAS TopLink Mapping Workbench project. The Remote, Local, Home, and localHome interfaces must also be on the classpath, because they may be used during EJB validation.