27 Introduction to Relational Mappings

A relational mapping transforms any object data member type to a corresponding relational database (SQL) data source representation in any supported relational database. Relational mappings let you map an object model into a relational data model.

Relational mappings transform object data members to relational database fields. Use them to map simple data types including primitives (such as int), JDK classes (such as String), and large object (LOB) values. You can also use them to transform object data members that reference other domain objects by way of association where data source representations require object identity maintenance (such as sequencing and back references) and possess various types of multiplicity and navigability. The appropriate mapping class is chosen primarily by the cardinality of the relationship.

27.2 Relational Mapping Concepts

27.2.1 Directionality

The direction of a relationship may be either unidirectional or bidirectional. In a unidirectional relationship, only one entity bean has a relationship field that refers to the other. All TopLink relational mappings are unidirectional, from the class being described (the source class) to the class with which it is associated (the target class). The target class does not have a reference to the source class in a unidirectional relationship.

In a bidirectional relationship, each entity bean has a relationship field that refers to the other bean. Through the relationship field, an entity bean's code can access its related object. To implement a bidirectional relationship (classes that reference each other), use two unidirectional mappings with the sources and targets reversed.

Note:

Maintenance of bidirectional relationships presents a number of technical challenges. For more information, see the following:

27.2.2.1 Using a Direct Mapping

27.2.2.2 Using a Converter Mapping

If the attribute type is comparable to a database type but requires conversion, the information can be stored directly by using a direct-to-field mapping (see Section 27.3, "Direct-to-Field Mapping") and an appropriate Converter instance.

In the previous release, TopLink provided subclasses of DirectToFieldMapping for object type direct mappings, serialized object direct mappings, and type conversion direct mappings. In this release, these subclasses are deprecated. In their place, Oracle recommends that you use the DirectToFieldMapping method setConverter and the corresponding Converter instance. Table 27-2 summarizes these changes.

If the application's objects contain attributes that cannot be represented as direct-to-field with an existing converter, use a direct-to-field mapping with a custom converter.

27.2.2.3 Using a Transformation Mapping

If there is no database primitive type that is logically comparable to the attribute's type, or, if an attribute requires data from multiple fields, it must be transformed on its way to and from the database.

27.3 Direct-to-Field Mapping

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

Example 27-1 Direct-to-Field Mapping Example

Figure 27-1 illustrates a direct-to-field mapping between the Java attribute city and the relational database column CITY. Similarly, direct-to-field mappings could be defined from country to COUNTRY, id to ADDRESS_ID, established to EST_DATE, and province to PROVINCE.

27.5 One-to-One Mapping

One-to-one mappings represent simple pointer references between two Java objects. In Java, a single pointer stored in an attribute represents the mapping between the source and target objects. Relational database tables implement these mappings using foreign keys.

Figure 27-2 illustrates a one-to-one relationship from the address attribute of an Employee object to an Address object. To store this relationship in the database, create a one-to-one mapping between the address attribute and the Address class. This mapping stores the id of the Address instance in the EMPLOYEE table when the Employee instance is written. It also links the Employee instance to the Address instance when the Employee is read from the database. Because an Address does not have any references to the Employee, it does not have to provide a mapping to Employee.

For one-to-one mappings, the source table normally contains a foreign key reference to a record in the target table. In Figure 27-2, the ADDR_ID field of the EMPLOYEE table is a foreign key.

You can also implement a one-to-one mapping where the target table contains a foreign key reference to the source table. In Figure 27-2, the database design would change such that the ADDRESS row would contain the EMP_ID to identify the Employee to which it belonged. In this case, the target must also have a relationship mapping to the source.

The update, insert and delete operations, which are normally done for the target before the source for privately owned one-to-one relationships, are performed in the opposite order when the target owns the foreign key. Target foreign keys normally occur in bidirectional one-to-one mappings (see Section 27.2.1, "Directionality"), because one side has a foreign key and the other shares the same foreign key in the other's table.

Target foreign keys can also occur when large cascaded composite primary keys exist (that is, one object's primary key is composed of the primary key of many other objects). In this case it is possible to have a one-to-one mapping that contains both foreign keys and target foreign keys.

In a foreign key, TopLink automatically updates the foreign key value in the object's row. In a target foreign key, it does not. In TopLink, use the Target Foreign Key option when a target foreign key relationship is defined.

When mapping a relationship, you must understand these differences between a foreign key and a target foreign key, to ensure that the relationship is defined correctly.

In a bidirectional relationship where the two classes in the relationship reference each other, only one of the mappings should have a foreign key. The other mapping should have a target foreign key. If one of the mappings in a bidirectional relationship is a one-to-many mapping, see Chapter 32, "Configuring a Relational Variable One-to-One Mapping" for details.

27.5.1One-to-One Mappings and EJB 2.n CMP

To maintain EJB compliance, the object attribute that points to the target of the relationship must be the local interface type–not the bean class.

TopLink provides variations on one-to-one mappings that lets you 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.

27.6 Variable One-to-One Mapping

Variable class relationships are similar to polymorphic relationships, except that in this case the target classes are not related through inheritance (and thus not good candidates for an abstract table), but through an interface.

To define variable class relationships in TopLink Workbench, use the variable one-to-one mapping selection, but choose the interface as the reference class. This makes the mapping a variable one-to-one. When defining mappings in Java code, use the VariableOneToOneMapping class.

TopLink supports variable relationships only in one-to-one mappings. It handles this relationship in two ways:

27.7 One-to-Many Mapping

One-to-many mappings are used to represent the relationship between a single source object and a collection of target objects. They are a good example of something that is simple to implement in Java using a Collection (or other collection types) of target objects, but difficult to implement using relational databases.

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

The purpose of creating this one-to-one mapping in the target is so that the foreign key information can be written when the target object is saved. Alternatives to the one-to-one mapping back reference include the following:

Use a direct-to-field mapping to map the foreign key and maintain its value in the application. Here the object model does not require a back reference, but the data model still requires a foreign key in the target table.

Use a many-to-many mapping to implement a logical one-to-many. This has the advantage of not requiring a back reference in the object model and not requiring a foreign key in the data model. In this model the many-to-many relation table stores the collection. It is possible to put a constraint on the join table to enforce that the relation is a logical one-to-many relationship.

27.7.1One-to-Many Mappings and EJB 2.n CMP

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 local interface type–not the bean class.

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

27.8 Many-to-Many Mapping

Many-to-many mappings represent the relationships between a collection of source objects and a collection of target objects. They require the creation of an intermediate table for managing the associations between the source and target records.

Figure 27-5 illustrates a many-to-many mapping in Java and in relational database tables.

Many-to-many mappings are implemented using a relation table. This table contains columns for the primary keys of the source and target tables. Composite primary keys require a column for each field of the composite key. The intermediate table must be created in the database before using the many-to-many mapping.

The target class does not have to implement any behavior for the many-to-many mappings. If the target class also creates a many-to-many mapping back to its source, then it can use the same relation table, but one of the mappings must be set to read-only. If both mappings write to the table, they can cause collisions.

Indirection (lazy loading) is enabled by default in a many-to-many mapping, which requires that the attribute have the ValueHolderInterface type or transparent collections. For more information on indirection, see Section 17.2.4, "Indirection (Lazy Loading)".

27.8.1 Many-to-Many Mappings and EJB 2.n CMP

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.

27.9 Aggregate Collection Mapping

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

Although aggregate collection mappings are similar to one-to-many mappings, they are not replacements for one-to-many mappings. Use aggregate collections only in situations where the target collections are of a reasonable size and if having a one-to-one back mapping is difficult.

Because oneEtoEmany relationships offer better performance and are more robust and scalable, consider using a oneEtoEmany 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.

27.9.1 Aggregate Collection Mappings and Inheritance

Aggregate collection descriptors can use inheritance. You must also declare subclasses as aggregate collection. The subclasses can have their own mapped tables, or share the table with their parent class. See Section 16.2.2, "Descriptors and Inheritance" for more information on inheritance.

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

To implement an aggregate collection mapping, the following must take place:

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.

27.9.2Aggregate Collection Mappings and EJB

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.

27.9.3 How to Implement Aggregate Collection Mappings

To implement an aggregate collection mapping, the following must take place:

The descriptor of the target class must declare itself to be 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.

27.10 Direct Collection Mapping

Direct collection mappings store collections of Java objects that are not TopLink-enabled. The object type stored in the direct collection is typically a Java type, such as String.

It is also possible to use direct collection mappings to map a collection of non-String objects. For example, it is possible to have an attribute that contains a collection of Integer or Date instances. The instances stored in the collection can be any type supported by the database and has a corresponding wrapper class in Java.

Support for primitive data types such as int is not provided, because Java Collection holds only objects.

Figure 27-6 illustrates how a direct collection is stored in a separate table with two fields. The first field is the reference key field, which contains a reference to the primary key of the instance owning the collection. The second field contains an object in the collection and is called the direct field. There is one record in the table for each object in the collection.

27.11 Direct Map Mapping

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.

Figure 27-7 illustrates how a direct map is stored in a separate table with three fields. The first field (EMPID) is the reference key field, which contains a reference to the primary key of the instance owning the collection. The second field (ADDRESS) contains an object in the collection and is called the direct value field. The third field (TYPE) contains the direct key field. In this example, the direct map uses a object type converter for the direct key field, converting the single character W in the database to the full string Work in the object (and H to Home).

27.12 Aggregate Object Mapping

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

An aggregate mapping allows you to associate data members in the target object with fields in the source object's underlying database tables.

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

You cannot configure one-to-one, one-to-many, or many-to-many mappings from a nonaggregate object to an aggregate target object.

You can configure such mappings from an aggregate target object to another nonaggregate object. If you configure a one-to-many mapping from an aggregate target object to another nonaggregate object, you must configure a one-to-one mapping from the other object back to the source object that owns the aggregate (instead of to the aggregate target object itself). This is because the source object contains the table and primary key information of the aggregate target.

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

When you configure the aggregate object mapping in the source object, you choose the source object table for that particular mapping. This allows different source types to store the same target information within their tables. Each source object's table may use different field names. TopLink automatically manages the case where multiple source object tables use different field names.

For example, in Figure 27-9, The Employee attribute employPeriod is mapped by an aggregate object mapping to target object Period. This mapping associates Period attribute startDate with EMPLOYEE table field START_DATE. The Project attribute projectPeriod is also mapped by an aggregate object mapping to target object Period. This mapping associates Period attribute startDate with PROJECT table field S_DATE.

27.12.3 How to Implement an Aggregate Object Relationship Mapping

You must ensure that the following takes place:

The descriptor of the target class declares itself to be an aggregate object. Because all its information comes from its parent's table(s), the target descriptor does not have a specific table associated with it. You must, however, choose one or more candidate table(s) from which you can use fields in mapping the target.

In the example above, you could choose the EMPLOYEE table so that the START_DATE and END_DATE fields are available during mapping.

The descriptor of the source class adds an aggregate object mapping that specifies the target class.

In the example above, the Employee class has an attribute called employPeriod that would be mapped as an aggregate object mapping with Period as the reference class.

The source class must ensure that its table has fields that correspond to the field names registered with the target class.

If a source object has a null target reference, TopLink writes NULLs to the aggregate database fields (see Section 37.3, "Configuring Allowing Null Values"). When the source is read from the database, it can handle this null target in one of two ways:

Create an instance of the object with all its attributes equal to null.

Put a null reference in the source object without instantiating a target. (This is the default method of handling null targets.)

27.13 Transformation Mapping

Use transformation mappings for specialized translations for how a value is represented in Java and how it is represented in the database.

Tip:

Use transformation mappings only when mapping multiple fields into a single attribute. Because of the complexity of transformation mappings, it is often easier to perform the transformation with a converter or getter and setter methods of a directEtoEfield mapping. See Chapter 29, "Configuring a Relational Direct-to-Field Mapping" for more information.

Figure 27-10 illustrates a transformation mapping. The values from the B_DATE and B_TIME fields are used to create a java.util.Date to be stored in the birthDate attribute.

Often, a transformation mapping is appropriate when values from multiple fields are used to create an object. This type of mapping requires that you provide an attribute transformation that is invoked when reading the object from the database. This must have at least one parameter that is an instance of Record. In your attribute transformation, you can use Record method get to retrieve the value in a specific column. Your attribute transformation can specify a second parameter, when it is an instance of Session. The Session performs queries on the database to get additional values needed in the transformation. The transformation should return the value to be stored in the attribute.

Transformation mappings also require a field transformation for each field, to be written to the database when the object is saved. The transformation returns the value to be stored in that field.