15.11. Field Mapping
Prev | Chapter 15. Mapping Metadata | Next

## 15.11. Field Mapping

The following sections enumerate the myriad of field mappings JDOR supports. JDOR augments standard persistence metadata's `field` element with many new object-relational attributes and sub-elements. As we explore the library of standard mappings, we introduce each of these enhancements in context.

| Note
Kodo allows you to create custom field mappings for unsupported field types or database schemas. See the Reference Guide's Chapter 7, Mapping for complete coverage of Kodo JDO's mapping capabilities.

### 15.11.1. Superclass Fields

As you may have already noticed from the mapping document we have been constructing throughout this chapter, subclasses can map superclass fields by setting the `field` element's `name` attribute to `<superclass-name>.<field-name>`. For example, `Subscription` maps its `Contract` superclass' `id` field:

    <class name="Subscription" table="CNTRCT.SUB">
        <field name="Contract.id" column="ID"/>
        ...
    </class>

If `Contract` were in a different package than `Subscription`, we would have to fully qualify `Contract`'s class name:

    <class name="Subscription" table="CNTRCT.SUB">
        <field name="org.mag.subscribe.Contract.id" column="ID"/>
        ...
    </class>

As we tour the standard JDOR field mappings throughout this chapter, keep in mind that any of them can be applied to a superclass field just as easily as to a field in the current class.

### 15.11.2. Basic Mapping

A basic field mapping stores the field value directly into a database column. Primitives, primitive wrappers, `String`s, `Date`s, `Locale`s, and any other supported type that can be easily translated into a native JDBC value default to basic mapping. In fact, you have already seen examples of basic field mappings in this chapter - for example, the mapping of `Magazine`'s primary key fields in Example 15.3, “Datastore Identity Mapping”.

To write a basic field mapping, just name the column that holds the field value. The `field` element accepts a `column` attribute or nested `column` elements for this purpose. We introduced columns and the attribute/element dichotomy already in Section 15.6, “Column”.

| Note
Kodo stores Java 5 `Enum` values by storing the value name in any `CHAR` or `VARCHAR` column. Storing the name is safer than storing the ordinal position, in case you reorder your enum values or add new options at the beginning or middle of the value list.

Below we present an updated diagram of our model and its associated database schema, followed by its representation as a mapping metadata document. All basic fields are now mapped.

Example 15.11. Basic Field Mapping

    <?xml version="1.0"?>
    <orm>
        <package name="org.mag">
            <class name="Magazine" table="MAG">
                <field name="isbn">
                    <column name="ISBN" jdbc-type="char" length="15"/>
                </field>
                <field name="title" column="TITLE"/>
                <field name="price" column="PRICE"/>
                <field name="copiesSold" column="COPIES"/>
                ...
            </class>
            <class name="Article" table="ART">
                <field name="id" column="ID"/>
                <field name="title" column="TITLE"/>
                ...
            </class>
        </package>
        <package name="org.mag.pub">
            <class name="Company" table="COMP">
                <field name="name" column="NAME"/>
                <field name="revenue" column="REV"/>
                ...
            </class>
            <class name="Author" table="AUTH">
                <field name="firstName" column="FNAME"/>
                <field name="lastName" column="LNAME"/>
                ...
            </class>
        </package>
        <package name="org.mag.subscribe">
            <class name="Subscription" table="CNTRCT.SUB">
                <field name="Contract.id" column="ID"/>
                <field name="startDate" column="START"/>
                <field name="payment" column="PAY"/>
                ...
            </class>
            <class name="LifetimeSubscription">
                <field name="eliteClub" column="ELITE"/>
                ...
            </class>
            <class name="TrialSubscription" table="CNTRCT.TRIAL_SUB">
                <field name="endDate" column="END"/>
                ...
            </class>
            <class name="Subscription$LineItem" table="CNTRCT.LINE_ITEM">
                <field name="Contract.id" column="ID"/>
                <field name="price" column="PRICE"/>
                <field name="num" column="NUM"/>
                ...
            </class>
        </package>
    </orm>

#### 15.11.2.1. CLOB

Unlike Java, most relational databases cannot transparently store strings of arbitrary length. The standard database string types `CHAR` and `VARCHAR` are often limited to holding 255 characters or less. To store longer strings, you must use the `CLOB` column type.

`CLOB` stands for Character Large OBject. In JDOR, mapping a Java `String` field to a database `CLOB` column is exactly like creating any other basic mapping. The only difference is that you must use the `column` element's `jdbc-type` attribute to tell the JDOR implementation to use JDBC's `CLOB` APIs rather than the standard string APIs.

Our sample model uses two large string fields: `Contract.terms` and `LineItem.comments`. Since `Contract` uses a `subclass-table` mapping, `LineItem` maps both these fields to its own table:

In mapping metadata, `CLOB` mapping looks like this:

Example 15.12. CLOB Field Mapping

    <class name="Subscription$LineItem" table="CNTRCT.LINE_ITEM">
        <field name="Contract.terms">
            <column name="TERMS" jdbc-type="clob"/>
        </field>
        <field name="comments">
            <column name="COMMENTS" jdbc-type="clob"/>
        </field>
        ...
    </class>

| Note
In Kodo, you can set the `column`'s `length` attribute to `-1` rather than setting its `jdbc-type` to `clob`. This approach allows Kodo to take advantage of some databases' ability to represent unlimited-length strings natively, without resorting to a `CLOB`. If your database does not support unlimited-length strings natively, Kodo falls back to `CLOB` handling.

#### 15.11.2.2. BLOB

`BLOBs` are Binary Large OBjects. JDOR uses `BLOB` columns to store `byte` arrays and serialized Java objects.

Mapping a `byte[]` field to a `BLOB` column is a basic mapping. For example, let's map our model's `Article.content` field to the `ART.CONTENT` `BLOB` column:

Example 15.13. Byte Array Field Mapping

    <class name="Article" table="ART">
        <field name="content" column="CONTENT"/>
        ...
    </class>

Mapping a serialized `Object` field to a `BLOB` column is like mapping a `byte[]` field, except that you must explicitly instruct JDOR to serialize the field value with the `serialized` attribute. Suppose that instead of a `byte[]`, our `Article.content` field is of type `Object`, and we want to serialize that `Object` to the `CONTENT` column:

Example 15.14. Serialized Field Mapping

    <class name="Article" table="ART">
        <field name="content" column="CONTENT" serialized="true"/>
        ...
    </class>

### 15.11.3. Automatic Values

In Section 15.5, “Datastore Identity”, you saw how to use the `datastore-identity` element's `strategy` and `sequence` attributes to automatically generate datastore identity values. You can apply the same pattern to auto-generate values for fields of new objects. The `field` element's `value-strategy` attribute accepts the same strategies as `datastore-identity`'s `strategy` attribute. The `field` element also has a `sequence` attribute that functions exactly like `datastore-identity`'s `sequence` attribute. Let's modify our mappings to set `Article`'s `id` field automatically from the `ArticleSeq` sequence, and to make the `LineNumber.num` field auto-incrementing:

Example 15.15. Automatic Field Values

    <?xml version="1.0"?>
    <orm>
        <package name="org.mag">
            <sequence name="ArticleSeq" datastore-sequence="ART_SEQ"/>
            <class name="Article" table="ART">
                <!-- specifying the sequence attribute defaults the -->
                <!-- value-strategy attribute to "sequence" automatically -->
                <field name="id" column="ID" sequence="ArticleSeq"/>
                ...
            </class>
            ...
        </package>
        <package name="org.mag.subscribe">
            <class name="Subscription$LineItem" table="CNTRCT.LINE_ITEM">
                <field name="num" column="NUM" value-strategy="autoassign"/>
                ...
            </class>
            ...
        </package>
    </orm>

| Note
Kodo supports the standard `sequence`, `identity`, `autoassign`, `uuid-string`, and `uuid-hex` field value strategies. Kodo also offers the custom `version` strategy. Kodo will automatically set a field with the `version` value strategy to the object's optimistic version value. The field must be a numeric or date type. If you use a version field, you should not specify a separate version mapping (we covered version mapping in Example 15.10, “Version Mapping”). Kodo also defines a `read-only` metadata extension that allows you to ignore or restrict updates to a persistent field. See Section 6.4.2.6, “Read-Only” in the Reference Guide for details. Fields that use the custom `version` value strategy are always read-only.

Using the `autoassign` or `identity` strategy on a field may cause the `PersistenceManager` to flush when retrieving the value of that field on a persistent-new instance. If the field is a primary key field, retrieving the object id of a persistent-new instance may also cause a flush, just as it does for new instances using the `autoassign` datastore identity strategy. See Section 15.5, “Datastore Identity” for the complete explanation.

### 15.11.4. Secondary Tables

Sometimes a a logical record is spread over multiple database tables. JDOR calls a class' declared table the primary table, and calls other tables that make up a logical record secondary tables. You can map any persistent field to a secondary table. Just write the standard field mapping, then perform these two additional steps:

1. Set the `field` element's `table` attribute to the name of the secondary table housing the mapped columns.

2. Nest a `join` element within the `field` that describes how the secondary table joins to the class' primary table. The `join` element goes before the field's nested `column` elements, if any. We covered joins in Section 15.7, “Joins”. In fact, we already used the `join` element to join a subclass table to its superclass table in Section 15.8.2, “new-table”.

   Secondary table joins, however, have one caveat that subclass table joins do not. In a subclass join, you know that there will always be a record in the superclass table for every record in the subclass table. In a secondary table join, that is not the case. Some databases are maintained such that primary table rows may not have matching secondary table rows. When you load objects based on these records, you want the fields mapped to the secondary table to come back `null`.

   Joins that produce `null`s for missing records are called outer joins. You may recall having read about another use for outer joins in Section 15.9.3, “none”. If your field is mapped to a secondary table that doesn't necessarily have a row for every primary table row, set the `join` element's `outer` attribute to `true`.

   | Note
   When the `outer` attribute is `true`, Kodo will not insert null data rows into the named secondary table. That is, if you are inserting a new object, and it has `null` values for every field mapped to an outer-joined secondary table, Kodo will not insert a row into that secondary table.

In the following example, we move the `Article.content` field we mapped in Section 15.11.2.2, “BLOB” into an outer-joined secondary table, like so:

Example 15.16. Secondary Table Field Mapping

    <class name="Article" table="ART">
        <field name="id" column="ID"/>
        <field name="content" table="ART_DATA" column="CONTENT">
            <join outer="true">
                <column name="ART_ID" target="ID"/>
            </join>
        </field>
        ...
    </class>

Because the target table `ART` only has a single primary key column, we can use join shortcuts to get rid of the nested `column` element. Our final mapping becomes:

    <class name="Article" table="ART">
        <field name="id" column="ID"/>
        <field name="content" table="ART_DATA" column="CONTENT">
            <join outer="true" column="ART_ID"/>
        </field>
        ...
    </class>

### 15.11.5. Direct Relations

A direct relation is a non-embedded persistent field that holds a reference to another persistence-capable object. Our model has three direct relations: `Magazine`'s `publisher` field is a direct relation to a `Company`, `Magazine`'s `coverArticle` field is a direct relation to `Article`, and the `LineItem.magazine` field is a direct relation to a `Magazine`. Direct relations are represented in the database by foreign key columns:

You should be familiar with foreign key mapping from Section 15.7, “Joins”. Additionally, we have already demonstrated mapping foreign key columns onto the `join` element in Section 15.8.2, “new-table” and Section 15.11.4, “Secondary Tables”. The only difference now is that direct relations map the foreign key columns directly onto the `field` element:

Example 15.17. Direct Relation Field Mapping

    <?xml version="1.0"?>
    <orm>
        <package name="org.mag">
            <class name="Magazine" table="MAG">
                <field name="isbn">
                    <column name="ISBN" jdbc-type="char" length="15"/>
                </field>
                <field name="title" column="TITLE"/>
                <field name="coverArticle">
                    <column name="COVER_ID" target="ID"/>
                </field>
                <field name="publisher">
                    <column name="PUB_ID" target="CID"/>
                </field>
                ...
            </class>
            <class name="Article" table="ART">
                <field name="id" column="ID"/>
                ...
            </class>
            ...
        </package>
        <package name="org.mag.pub">
            <class name="Company" table="COMP">
                <datastore-identity column="CID"/>
                ...
            </class>
            ...
        </package>
        <package name="org.mag.subscribe">
            <class name="Subscription$LineItem" table="CNTRCT.LINE_ITEM">
                <field name="magazine">
                    <column name="MAG_ISBN" target="ISBN"/>
                    <column name="MAG_TITLE" target="TITLE"/>
                </field>
                ...
            </class>
            ...
        </package>
    </orm>

Or, after applying syntactic shortcuts:

    <?xml version="1.0"?>
    <orm>
        <package name="org.mag">
            <class name="Magazine" table="MAG">
                <field name="isbn">
                    <column name="ISBN" jdbc-type="char" length="15"/>
                </field>
                <field name="title" column="TITLE"/>
                <field name="coverArticle" column="COVER_ID"/>
                <field name="publisher" column="PUB_ID"/>
                ...
            </class>
            <class name="Article" table="ART">
                <field name="id" column="ID"/>
                ...
            </class>
            ...
        </package>
        <package name="org.mag.pub">
            <class name="Company" table="COMP">
                <datastore-identity column="CID"/>
                ...
            </class>
            ...
        </package>
        <package name="org.mag.subscribe">
            <class name="Subscription$LineItem" table="CNTRCT.LINE_ITEM">
                <field name="magazine">
                    <column name="MAG_ISBN" target="ISBN"/>
                    <column name="MAG_TITLE" target="TITLE"/>
                </field>
                ...
            </class>
            ...
        </package>
    </orm>

| Note
A direct relation between types `A` and `B` is also called a one-to-one relation when every `A` has a unique `B`. It is called a many-to-one relation when multiple `A`s can refer to the same `B`.

#### 15.11.5.1. Inverse Keys

The direct relations above are all based on forward foreign keys. But relations can also be based on inverse keys. We described inverse foreign keys in Section 15.7, “Joins”.

For example, suppose that instead of `Magazine` having a relation to its cover `Article`, `Article` has a relation to the `Magazine` it serves as the cover of. We can represent this relation without changing our schema:

Example 15.18. Inverse Key Relation Field Mapping

Notice that the `Article.coverOf` field refers to the foreign key column in the `MAG` table by using the fully qualified column name.

    <class name="Magazine" table="MAG">
        ...
    </class>
    <class name="Article" table="ART">
        <field name="id" column="ID"/>
        <field name="coverOf">
            <column name="MAG.PUB_ID" target="ID"/>
        </field>
        ...
    </class>

After syntactic shortuts:

    <class name="Magazine" table="MAG">
        ...
    </class>
    <class name="Article" table="ART">
        <field name="id" column="ID"/>
        <field name="coverOf" column="MAG.PUB_ID"/>
        ...
    </class>

#### 15.11.5.2. Bidirectional Relations

In the previous sections, we saw that a single foreign key from the `MAG` table to the `ART` table can model an object relation from `Magazine` to `Article` (through the `Magazine.coverArticle` field), or from `Article` to `Magazine` (though the `Article.coverOf` field). A natural question is whether the foreign key can represent both relations at the same time. The answer is yes.

When two fields are logical inverses of each other like `Magazine.coverArticle` and `Article.coverOf`, they form a bidirectional relation. And when the two fields of a bidirectional relation share the same database mapping, JDOR formalizes the connection with the `mapped-by` field attribute. Using the `mapped-by` attribute, we can map both `Magazine.coverArticle` and `Article.coverOf` as follows:

Example 15.19. Mapping a Bidirectional Relation

    <class name="Magazine" table="MAG">
        <field name="coverArticle" column="PUB_ID"/>
        ...
    </class>
    <class name="Article" table="ART">
        <field name="coverOf" mapped-by="coverArticle"/>
        ...
    </class>

Marking `Article.coverOf` as `mapped-by` `Magazine.coverArticle` means two things:

1. `Article.coverOf` uses the foreign key mapped by `Magazine.coverArticle`, but inverses it. In fact, it is illegal to specify any additional mapping information when you use the `mapped-by` attribute. All mapping information is read from the referenced field.

2. `Magazine.coverArticle` is the "owner" of the relation. The field that specifies the mapping data is always the owner. This means that changes to the `Magazine.coverArticle` field are reflected in the database, while changes to the `Article.coverOf` field alone are not. Changes to `Article.coverOf` may still affect the JDOR implementation's cache, however. Thus, it is very important that you keep your object model consistent by properly maintaining both sides of your bidirectional relations at all times.

   | Note
   It is more efficient to make the field that maps to the foreign key's natural forward direction the owner of a bidirectional relation. Specify the mapping data on the forward foreign key side, and use `mapped-by` on the inverse foreign key side, just as we did with `Magazine.coverArticle` and `Article.coverOf`.

You should always take advantage of the `mapped-by` attribute rather than mapping each field of a bidirectional relation independently. Failing to do so may result in the JDOR implementation trying to update the database with conflicting data. Be careful to only mark one side of the relation as `mapped-by`, however. One side has to actually do the mapping!

| Note
You can configure Kodo to automatically synchronize both sides of a bidirectional relation, or to perform various actions when it detects inconsistent relations. See Section 5.4, “Managed Inverses” in the Reference Guide for details.

### 15.11.6. Basic Collections

Collections and arrays of primitive wrappers, `String`s, `Date`s, or anything else that can be directly stored in a database column all fall under the umbrella of basic collection mapping. Basic collection mapping is just a special case of secondary table mapping, as seen in Section 15.11.4, “Secondary Tables”. Basic collections map to a secondary table consisting of three components:

1. Foreign key columns linking back to the owning class' primary table. As with all secondary table mappings, these foreign key columns are mapped to a `join` element.

2. A column to hold a collection element. Each row of the collection table holds a single collection or array element. Logically enough, the `element` element represents a collection or array element.

3. An optional ordering column. Relational databases do not preserve record order. This column stores the relative position of elements within the collection or array so that the JDOR implementation can retrieve them in the same order they were in when stored. The `order` element anchors ordering column information in mapping metadata.

| Note
In addition to supporting an ordering column, Kodo allows you to order on the collection element values, or, in the case of relations, fields of the related type. See Section 6.4.2.3, “Order-By” in the Reference Guide for details.

The `Article.subtitles` field is the only basic collection in our model. This `List` of `String`s is mapped as follows:

In mapping metadata, this becomes:

Example 15.20. Basic Collection Field Mapping

    <class name="Article" table="ART">
        <field name="id" column="ID"/>
        <field name="subtitles" table="ART_SUBS">
            <join>
                <column name="ART_ID" target="ID"/>
            </join>
            <element column="SUBTITLE"/>
            <order column="ORD"/>
        </field>
        ...
    </class>

Taking advantage of join shortuts yields:

    <class name="Article" table="ART">
        <field name="id" column="ID"/>
        <field name="subtitles" table="ART_SUBS">
            <join column="ART_ID"/>
            <element column="SUBTITLE"/>
            <order column="ORD"/>
        </field>
        ...
    </class>

The same pattern applies to mapping collections of BLOBs or CLOBs, or any elements that can be stored in one or more data columns.

### 15.11.7. Association Table Collections

An association table consists of two foreign keys, plus an optional ordering column. In other words, if you replace the data columns of a basic collection table (Section 15.11.6, “Basic Collections”) with another foreign key, you produce an association table. As its name implies, each row of an association table associates two objects together. JDOR uses association tables to represent collections of persistence-capable objects: one foreign key refers back to the collection's owner, and the other refers to a collection element. Our model's `Magazine.articles` and `Company.subscriptions` fields both have association table mappings.

| Note
An association table relation between types `A` and `B` is also called a one-to-many relation when every `A` has a unique set of `B`s. It is called a many-to-many relation when multiple `A`s can refer to the same `B`.

In metadata, an association table mapping is exactly like a basic collection mapping (Section 15.11.6, “Basic Collections”), except that the `element` columns represent a foreign key. You should be very familiar with foreign key mapping by now. See Section 15.7, “Joins” for a refresher.

Example 15.21. Association Table Collection Field Mapping

    <?xml version="1.0"?>
    <orm>
        <package name="org.mag">
            <class name="Magazine" table="MAG">
                <field name="isbn">
                    <column name="ISBN" jdbc-type="char" length="15"/>
                </field>
                <field name="title" column="TITLE"/>
                <field name="articles" table="MAG_ARGS">
                    <join>
                        <column name="MAG_ISBN" target="ISBN"/>
                        <column name="MAG_TITLE" target="TITLE"/>
                    </join>
                    <element>
                        <column name="ART_ID" target="ID"/>
                    </element>
                </field>
                ...
            </class>
            <class name="Article" table="ART">
                <field name="id" column="ID"/>
                ...
            </class>
            ...
        </package>
        <package name="org.mag.pub">
            <class name="Company" table="COMP">
                <datastore-identity column="CID" strategy="autoassign"/>
                <field name="subscriptions" table="COMP_SUBS">
                    <join>
                        <column name="COMP_ID" target="CID"/>
                    </join>
                    <element>
                        <column name="SUB_ID" target="ID"/>
                    </element>
                </field>
                ...
            </class>
            ...
        </package>
        <package name="org.mag.subscribe">
            <class name="Subscription" table="CNTRCT.SUB">
                <field name="Contract.id" column="ID"/>
                ...
            </class>
            ...
        </package>
    </orm>

Applying join shortcuts yields:

    <?xml version="1.0"?>
    <orm>
        <package name="org.mag">
            <class name="Magazine" table="MAG">
                <field name="isbn">
                    <column name="ISBN" jdbc-type="char" length="15"/>
                </field>
                <field name="title" column="TITLE"/>
                <field name="articles" table="MAG_ARGS">
                    <join>
                        <column name="MAG_ISBN" target="ISBN"/>
                        <column name="MAG_TITLE" target="TITLE"/>
                    </join>
                    <element column="ART_ID"/>
                </field>
                ...
            </class>
            <class name="Article" table="ART">
                <field name="id" column="ID"/>
                ...
            </class>
            ...
        </package>
        <package name="org.mag.pub">
            <class name="Company" table="COMP">
                <datastore-identity column="CID" strategy="autoassign"/>
                <field name="subscriptions" table="COMP_SUBS">
                    <join column="COMP_ID"/>
                    <element column="SUB_ID"/>
                </field>
                ...
            </class>
            ...
        </package>
        <package name="org.mag.subscribe">
            <class name="Subscription" table="CNTRCT.SUB">
                <field name="Contract.id" column="ID"/>
                ...
            </class>
            ...
        </package>
    </orm>

None of our model's association table collections are ordered, but if they were, we could use the `order` mapping element to specify the ordering column, just as in a basic collection mapping.

#### 15.11.7.1. Bidirectional Relations

Association tables have no built-in sense of direction. Each row links two objects, period. You can look at the `MAG_ARTS` table in our example above as linking each `Magazine` to its `Article`s, or as linking each `Article` to the `Magazine`s it appears in. The two outlooks are equally valid. This makes association tables perfect candidates for representing bidirectional relations.

Section 15.11.5.2, “Bidirectional Relations” examined direct bidirectional relations that share a foreign key. Association table bidirectional relations share association table rows instead. Aside from that, all the same concepts apply. In particular, you still use the `mapped-by` attribute to mark one collection field as mapped and "owned" by another collection field. The owning field is responsible for manipulating association table records - the same caveats about keeping your bidirectional relations synchronized apply equally to collections.

In the example below, we have added an `Article.magazines` field as the logical inverse of the the `Magazine.articles` field.

Example 15.22. Mapping Bidirectional Association Table Relations

    <?xml version="1.0"?>
    <orm>
        <package name="org.mag">
            <class name="Magazine" table="MAG">
                <field name="isbn">
                    <column name="ISBN" jdbc-type="char" length="15"/>
                </field>
                <field name="title" column="TITLE"/>
                <field name="articles" table="MAG_ARGS">
                    <join>
                        <column name="MAG_ISBN" target="ISBN"/>
                        <column name="MAG_TITLE" target="TITLE"/>
                    </join>
                    <element column="ART_ID"/>
                </field>
                ...
            </class>
            <class name="Article" table="ART">
                <field name="id" column="ID"/>
                <field name="magazines" mapped-by="articles"/>
                ...
            </class>
            ...
        </package>
    </orm>

| Note
Association tables are symmetric; therefore, the choice of owning vs. non-owning side is arbitrary. However, only the owning side of the relation can order the its collection elements.

### 15.11.8. Inverse Key Collections

Inverse key collection mappings are the only standard collection mappings that do not use a secondary table. Instead, the mapping is based on an inverse foreign key in the table of the related class. Rather than try to explain the mapping abstractly, let's jump to an example.

| Note
Inverse key collection relations are also called one-to-many relations.

The `Subscription.items` field in our model is an inverse key collection. `Subscription.items` is a `Collection` of `LineItem` objects. There is a foreign key from `LineItem`'s table, `CNTRCT.LINE_ITEM`, to `Subscription`'s table, `CNTRCT.SUB`. The relation is loaded by selecting all `LineItem` records whose foreign key links back to the owning `Subscription`'s record.

This situation may look familiar to you. It is, in fact, exactly like the inverse key direct relation from Section 15.11.5.1, “Inverse Keys”. The only thing separating this mapping from the inverse key direct relation mapping is that in this case, multiple rows in `CNTRCT.LINE_ITEM` might have a foreign key to the same `CNTRCT.SUB` record. At the object level, this results in a collection rather than a direct reference.

This difference is also reflected in the mapping metadata. While direct references are mapped to `field`, collection elements are mapped to `field`'s nested `element` element. Also, you do not have to fully qualify the name of the foreign key columns in an inverse key collection, as you do in an inverse key direct relation. That is because the lack of a secondary table is a hint to the JDOR implementation that you are using an inverse key collection, and it knows to look in the table of the related class for the foreign key columns.

Example 15.23. Inverse Key Collection Field Mapping

    <?xml version="1.0"?>
    <orm>
        <package name="org.mag.subscribe">
            <class name="Subscription" table="CNTRCT.SUB">
                <field name="Contract.id" column="ID"/>
                <field name="items">
                    <element>
                        <column name="SUB_ID" target="ID"/>
                    </element>
                </field>
                ...
            </class>
            <class name="Subscription$LineItem" table="CNTRCT.LINE_ITEM">
                <field name="Contract.id" column="ID"/>
                ...
            </class>
            ...
        </package>
    </orm>

Or with join shortcuts in place:

    <?xml version="1.0"?>
    <orm>
        <package name="org.mag.subscribe">
            <class name="Subscription" table="CNTRCT.SUB">
                <field name="Contract.id" column="ID"/>
                <field name="items">
                    <element column="SUB_ID"/>
                </field>
                ...
            </class>
            <class name="Subscription$LineItem" table="CNTRCT.LINE_ITEM">
                <field name="Contract.id" column="ID"/>
                ...
            </class>
            ...
        </package>
    </orm>

Though it is rare, you can map an order column to an inverse key collection to maintain collection order. You use the `order` mapping element, as in any other collection mapping. The order column must be in the same table as the foreign key column(s).

| Note
Kodo allows you to specify an order column even in bidirectional inverse key collection mappings.

#### 15.11.8.1. Bidirectional Relations

Most inverse key collections are actually half of a bidirectional relation. The other half is a standard direct relation mapping (Section 15.11.5, “Direct Relations”) onto the foreign key. In fact, it is unusual to write an inverse key collection mapping as we did above; the vast majority of inverse key collections simply use the `mapped-by` attribute introduced in Section 15.11.5.2, “Bidirectional Relations” to refer to their partner field. Our model's `Magazine.publisher` and `Company.magazines` fields are perfect illustrations of the normal pairing between a direct relation and an inverse key collection.

Example 15.24. Direct Relation / Inverse Key Collection Pair

    <?xml version="1.0"?>
    <orm>
        <package name="org.mag">
            <class name="Magazine" table="MAG">
                <field name="publisher" column="PUB_ID"/>
                ...
            </class>
            ...
        </package>
        <package name="org.mag.pub">
            <class name="Company" table="COMP">
                <datastore-identity column="CID"/>
                <field name="mags" mapped-by="publisher"/>
                ...
            </class>
            ...
        </package>
    </orm>

### 15.11.9. Maps

JDOR supports maps of all kinds. Your map keys can be basic types like `String`s, more interesting types like BLOBs and CLOBs, or even persistence-capable objects. Map values have equal flexibility. Rather than treat each combination of key and value types as a separate mapping, this section explains the concepts of map mapping in JDOR. Armed with a solid understanding of map concepts, you will be able to write a mapping for any combination of key and value classes, because object-relational mapping in JDOR is remarkably consistent.

There are two ways to represent a map. The first is to write the map to a secondary table. You may recall secondary table mapping of singular values from Section 15.11.4, “Secondary Tables”. In a map's secondary table, each row represents a map entry. Therefore, every map's secondary table has the following three components:

1. Foreign key columns linking back to the owning class' primary table. As with all secondary table mappings, these foreign key columns are mapped to a `join` element.

2. Columns to hold the entry's key. If the map uses a simple key type like `String`, these columns will be standard data columns. If the map uses persistence-capable objects as keys, these will be foreign key columns linking to the records for those objects. Key columns are anchored to to a `key` element nested in the `field` element.

3. Columns to hold the entry's value. Like key columns, value columns can be direct data columns or foreign key columns, depending on the map's value type. You specify map value columns on the `field`'s `value` element.

In short, secondary table map mapping is like a combination of basic collection mapping (Section 15.11.6, “Basic Collections”) and association table collection mapping (Section 15.11.7, “Association Table Collections”), depending on the key and value types involved.

`Article.authors` is the lone map field in our model. For each author of the article, this field maps the author's last name to the persistent `Author` instance.

To translate this to mapping metadata, we perform a basic secondary table mapping, then add the key columns to the `key` element and the value foreign key columns to the `value` element.

Example 15.25. Map Field Mapping

    <?xml version="1.0"?>
    <orm>
        <package name="org.mag">
            <class name="Article" table="ART">
                <field name="id" column="ID"/>
                <field name="authors" table="ART_AUTHS">
                    <join>
                        <column name="ART_ID" target="ID"/>
                    </join>
                    <key column="LNAME"/>
                    <value>
                        <column name="AUTH_ID" target="AID"/>
                    </value>
                </field>
                ...
            </class>
            ...
        </package>
        <package name="org.mag.pub">
            <class name="Author" table="AUTH">
                <datastore-identity sequence="AuthorSeq">
                    <column name="AID" sql-type="INTEGER64"/>
                </datastore-identity>
                ...
            </class>
            ...
        </package>
    </orm>

Or with join shortuts in place:

    <?xml version="1.0"?>
    <orm>
        <package name="org.mag">
            <class name="Article" table="ART">
                <field name="id" column="ID"/>
                <field name="authors" table="ART_AUTHS">
                    <join column="ART_ID"/>
                    <key column="LNAME"/>
                    <value column="AUTH_ID"/>
                </field>
                ...
            </class>
            ...
        </package>
        <package name="org.mag.pub">
            <class name="Author" table="AUTH">
                <datastore-identity>
                    <column name="AID" sql-type="INTEGER64"/>
                </datastore-identity>
                ...
            </class>
            ...
        </package>
    </orm>

The second way to represent a map in JDOR is as a set of persistence-capable values with derived keys. In the example above, the key for each `Author` is her last name. So rather than create a secondary table with a dedicated key column, we can simply map the `Author` values, then declare that the key is mapped by each `Author`'s last name. This lets us get rid of the key column altogether.

Example 15.26. Derived Key Mapping

    <?xml version="1.0"?>
    <orm>
        <package name="org.mag">
            <class name="Article" table="ART">
                <field name="id" column="ID"/>
                <field name="authors" table="ART_AUTHS">
                    <join column="ART_ID"/>
                    <key mapped-by="lastName"/>
                    <value column="AUTH_ID"/>
                </field>
                ...
            </class>
            ...
        </package>
        <package name="org.mag.pub">
            <class name="Author" table="AUTH">
                <datastore-identity>
                    <column name="AID" sql-type="INTEGER64"/>
                </datastore-identity>
                ...
            </class>
            ...
        </package>
    </orm>

When the key is derived from a field of the value type, you aren't limited to using a secondary table to hold the map. In fact, you can take any mapping that works for a collection of persistence-capable objects and turn it into a map mapping in two steps:

1. Add a `key` XML element. Set its `mapped-by` attribute to the name of a field in the persistence-capable value class. JDOR will automatically map each entry on the value of this field.

2. Use the `value` XML element instead of the `element` collection element to map the related persistence-capable objects.

The diagram above depicts the mapping of a `Subscription`'s `LineItem`s. We created this mapping in Section 15.11.8, “Inverse Key Collections”. The example below turns this mapping into a map. Each map entry associates a `LineItem`'s `num` field value to that `LineItem`.

Example 15.27. Inverse Foreign Key Map Mapping

    public class Subscription
        extends Contract
    {
        private Map<Long,LineItem> lineItems;
        ...
    }

    <?xml version="1.0"?>
    <orm>
        <package name="org.mag.subscribe">
            <class name="Subscription" table="CNTRCT.SUB">
                <field name="Contract.id" column="ID"/>
                <field name="items">
                    <key mapped-by="num"/>
                    <value column="SUB_ID"/>
                </field>
                ...
            </class>
            <class name="Subscription$LineItem" table="CNTRCT.LINE_ITEM">
                <field name="Contract.id" column="ID"/>
                ...
            </class>
            ...
        </package>
    </orm>

### 15.11.10. Embedded Objects

Chapter 5, Metadata describes JDO's concept of embedded objects. The field values of embedded objects are stored as part of the owning record, rather than as a separate database record. Thus, instead of mapping a relation to an embedded object as a foreign key, you map all the fields of the embedded instance to columns in the owning field's table.

To perform an embedded mapping, nest the `embedded` element in the embedded `field`. This element has the following attributes:

- `null-indicator-column`: Set this attribute to the name of a column that indicates whether the embedded instance is null. When the JDOR implementation retrieves the embedded object, it will check this column first. If the column value is `null`, the JDOR runtime will load `null` into the field that owns the embedded object. If the column value is not `null`, the JDOR runtime will set the owning field to a new embedded object instance, and will load that instance's fields with data from their mapped columns.

  The null indicator column can be a column that is also mapped to an embedded field, or can be an artificial column whose sole purpose is to indicate whether the embedded instance is null. In the latter case, the JDOR implementation will set this column automatically.

  If you do not specify a null indicator column, then an embedded instance will never get loaded as `null`. Even if you commit an owning object with its embedded field set to `null`, the next time you re-read the owning object its embedded field will get loaded with a non-null embedded instance. The embedded instance will have default values for all of its fields.

Within the `embedded` element, nest `field` elements for all the fields of the embedded object. Map these fields as you would any other fields; you can even map recursive embedded fields, to arbitrary depth.

In our model, `Company` embeds its `Address`, as pictured in the diagram above. `Author` also embeds its `Address`. Let's see how this looks in mapping metadata:

Example 15.28. Embedded Field Mapping

This example uses the same column names for `Address`'s embedded fields in both the `COMP` and `AUTH` tables. Note, though, that nothing prevents you from using completely different mappings each time you embed an object.

    <?xml version="1.0"?>
    <orm>
        <package name="org.mag.pub">
            <class name="Company" table="COMP">
                <field name="address">
                    <embedded null-indicator-column="STREET">
                        <field name="street" column="STREET"/>
                        <field name="city" column="CITY"/>
                        <field name="state">
                            <column name="STATE" jdbc-type="char" length="2"/>
                        </field>
                        <field name="zip" column="ZIP"/>
                    </embedded>
                </field>
                ...
            </class>
            <class name="Author" table="AUTH">
                <field name="address">
                    <embedded null-indicator-column="STREET">
                        <field name="street" column="STREET"/>
                        <field name="city" column="CITY"/>
                        <field name="state">
                            <column name="STATE" jdbc-type="char" length="2"/>
                        </field>
                        <field name="zip" column="ZIP"/>
                    </embedded>
                </field>
                ...
            </class>
            ...
        </package>
    </orm>

We set the null indicator column to `STREET`. Any time the `STREET` column is `null`, JDOR loads `null` into the `address` field. Any time `STREET` is not null, JDOR loads a new `Address` instance into the `address` field, and populates that `Address` with data based on the above field mappings.

This section focused on fields that hold a direct reference to an embedded object. However, JDOR also supports collections that contain embedded object elements, and maps that contains embedded object keys, values, or both. In these cases, the columns for the embedded object's fields are part of the collection or map table. Mapping embedded elements, keys, or values is exactly like mapping an embedded field, except that instead of nesting the `embedded` element under the `field` element, you nest it under the `element`, `key`, or `value` elements, respectively. For example, if `Company` had a collection of embedded addresses, like so:

Then the mapping would be:

Example 15.29. Embedded Collection Elements

    <class name="Company" table="COMP">
        <datastore-identity column="CID" strategy="autoassign"/>
        <field name="addresses" table="COMP_ADDRS">
            <join column="COMP_ID"/>
            <element>
                <embedded>
                    <field name="street" column="STREET"/>
                    <field name="city" column="CITY"/>
                    <field name="state">
                        <column name="STATE" jdbc-type="char" length="2"/>
                    </field>
                    <field name="zip" column="ZIP"/>
                </embedded>
            </element>
        </field>
        ...
    </class>

Normally, you will not use a null indicator column for embedded collection elements, map keys, and map values. Only do so if you plan on storing null values in your collection or map.

Prev | Up | Next
15.10. Version | Home | 15.12. Foreign Keys