8. Field Mapping
Prev | Chapter 13. Mapping Metadata | Next

## 8. Field Mapping

The following sections enumerate the myriad of field mappings JPA supports. JPA augments the persistence metadata covered in Chapter 5, Metadata with many new object-relational annotations. As we explore the library of standard mappings, we introduce each of these enhancements in context.

### Note

OpenJPA supports many additional field types, and allows you to create custom mappings for unsupported field types or database schemas. See the Reference Guide's Chapter 7, Mapping for complete coverage of OpenJPA's mapping capabilities.

### 8.1. Basic Mapping

A basic field mapping stores the field value directly into a database column. The following field metadata types use basic mapping. These types were defined in Section 2, “ Field and Property Metadata ”.

- `Id` fields.

- `Version` fields.

- `Basic` fields.

In fact, you have already seen examples of basic field mappings in this chapter - the mapping of all identity fields in Example 13.3, “ Identity Mapping ”. As you saw in that section, to write a basic field mapping you use the `Column` annotation to describe the column the field value is stored in. We discussed the `Column` annotation in Section 3, “ Column ”. Recall that the name of the column defaults to the field name, and the type of the column defaults to an appropriate type for the field type. These defaults allow you to sometimes omit the annotation altogether.

#### 8.1.1. LOBs

Adding the `Lob` marker annotation to a basic field signals that the data is to be stored as a LOB (Large OBject). If the field holds string or character data, it will map to a `CLOB` (Character Large OBject) database column. If the field holds any other data type, it will be stored as binary data in a `BLOB` (Binary Large OBject) column. The implementation will serialize the Java value if needed.

The equivalent XML element is `lob`, which has no children or attributes.

### Note

OpenJPA also supports LOB streaming. See Section 7.11, “ LOB Streaming ” in the Reference Guide for details.

#### 8.1.2. Enumerated

You can apply the `Enumerated` annotation to your `Enum` fields to control how they map to the database. The `Enumerated` annotation's value one of the following constants from the `EnumType` enum:

- `EnumType.ORDINAL`: The default. The persistence implementation places the ordinal value of the enum in a numeric column. This is an efficient mapping, but may break if you rearrange the Java enum declaration.

- `EnumType.STRING`: Store the name of the enum value rather than the ordinal. This mapping uses a `VARCHAR` column rather than a numeric one.

The `Enumerated` annotation is optional. Any un-annotated enumeration field defaults to `ORDINAL` mapping.

The corresponding XML element is `enumerated`. Its embedded text must be one of `STRING` or `ORIDINAL`.

#### 8.1.3. Temporal Types

The `Temporal` annotation determines how the implementation handles your basic `java.util.Date` and `java.util.Calendar` fields at the JDBC level. The `Temporal` annotation's value is a constant from the `TemporalType` enum. Available values are:

- `TemporalType.TIMESTAMP`: The default. Use JDBC's timestamp APIs to manipulate the column data.

- `TemporalType.DATE`: Use JDBC's SQL date APIs to manipulate the column data.

- `TemporalType.TIME`: Use JDBC's time APIs to manipulate the column data.

If the `Temporal` annotation is omitted, the implementation will treat the data as a timestamp.

The corresponding XML element is `temporal`, whose text value must be one of: `TIME`, `DATE`, or `TIMESTAMP`.

#### 8.1.4. The Updated Mappings

Below we present an updated diagram of our model and its associated database schema, followed by the corresponding mapping metadata. Note that the mapping metadata relies on defaults where possible. Also note that as a mapped superclass, `Document` can define mappings that will automatically transfer to its subclass' tables. In Section 8.3, “ Embedded Mapping ”, you will see how a subclass can override its mapped superclass' mappings.

Example 13.10. Basic Field Mapping

    package org.mag;

    @Entity
    @IdClass(Magazine.MagazineId.class)
    @Table(name="MAG")
    @DiscriminatorValue("Mag")
    public class Magazine {

        @Column(length=9)
        @Id private String isbn;
        @Id private String title;

        @Column(name="VERS")
        @Version private int version;

        private String name;
        private double price;

        @Column(name="COPIES")
        private int copiesSold;

        ...

        public static class MagazineId {
            ...
        }
    }

    @Entity
    @Table(name="ART", uniqueConstraints=@Unique(columnNames="TITLE"))
    @SequenceGenerator(name="ArticleSeq", sequenceName="ART_SEQ")
    public class Article {

        @Id
        @GeneratedValue(strategy=GenerationType.SEQUENCE, generator="ArticleSeq")
        private long id;

        @Column(name="VERS")
        @Version private int version;

        private String title;
        private byte[] content;

        ...
    }

    package org.mag.pub;

    @Entity
    @Table(name="COMP")
    public class Company {

        @Column(name="CID")
        @Id private long id;

        @Column(name="VERS")
        @Version private int version;

        private String name;

        @Column(name="REV")
        private double revenue;

        ...
    }

    @Entity
    @Table(name="AUTH")
    public class Author {

        @Id
        @GeneratedValue(strategy=GenerationType.TABLE, generator="AuthorGen")
        @TableGenerator(name="AuthorGen", table="AUTH_GEN", pkColumnName="PK",
            valueColumnName="AID")
        @Column(name="AID", columnDefinition="INTEGER64")
        private long id;

        @Column(name="VERS")
        @Version private int version;

        @Column(name="FNAME")
        private String firstName;

        @Column(name="LNAME")
        private String lastName;

        ...
    }

    @Embeddable
    public class Address {
        ...
    }

    package org.mag.subscribe;

    @MappedSuperclass
    public abstract class Document {

        @Id
        @GeneratedValue(strategy=GenerationType.IDENTITY)
        private long id;

        @Column(name="VERS")
        @Version private int version;

        ...
    }

    @Entity
    @Table(schema="CNTRCT")
    @Inheritance(strategy=InheritanceType.JOINED)
    @DiscriminatorColumn(name="CTYPE")
    public class Contract
        extends Document {

        @Lob
        private String terms;

        ...
    }

    @Entity
    @Table(name="SUB", schema="CNTRCT")
    @DiscriminatorColumn(name="KIND", discriminatorType=DiscriminatorType.INTEGER)
    @DiscriminatorValue("1")
    public class Subscription {

        @Id
        @GeneratedValue(strategy=GenerationType.IDENTITY)
        private long id;

        @Column(name="VERS")
        @Version private int version;

        @Column(name="START")
        private Date startDate;

        @Column(name="PAY")
        private double payment;

        ...

        @Entity
        @Table(name="LINE_ITEM", schema="CNTRCT")
        public static class LineItem
            extends Contract {

            @Column(name="COMM")
            private String comments;

            private double price;
            private long num;
            ...
        }
    }

    @Entity(name="Lifetime")
    @DiscriminatorValue("2")
    public class LifetimeSubscription
        extends Subscription {

        @Basic(fetch=FetchType.LAZY)
        @Column(name="ELITE")
        private boolean getEliteClub() { ... }
        public void setEliteClub(boolean elite) { ... }

        ...
    }

    @Entity(name="Trial")
    @DiscriminatorValue("3")
    public class TrialSubscription
        extends Subscription {

        @Column(name="END")
        public Date getEndDate() { ... }
        public void setEndDate(Date end) { ... }

        ...
    }

The same metadata expressed in XML:

    <entity-mappings xmlns="http://java.sun.com/xml/ns/persistence/orm"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="http://java.sun.com/xml/ns/persistence/orm orm_1_0.xsd"
        version="1.0">
        <mapped-superclass class="org.mag.subscribe.Document">
            <attributes>
                <id name="id">
                    <generated-value strategy="IDENTITY"/>
                </id>
                <version name="version">
                    <column name="VERS"/>
                </version>
                ...
            </attributes>
        </mapped-superclass>
        <entity class="org.mag.Magazine">
            <table name="MAG"/>
            <id-class="org.mag.Magazine.MagazineId"/>
            <discriminator-value>Mag</discriminator-value>
            <attributes>
                <id name="isbn">
                    <column length="9"/>
                </id>
                <id name="title"/>
                <basic name="name"/>
                <basic name="price"/>
                <basic name="copiesSold">
                    <column name="COPIES"/>
                </basic>
                <version name="version">
                    <column name="VERS"/>
                </version>
                ...
            </attributes>
        </entity>
        <entity class="org.mag.Article">
            <table name="ART">
                <unique-constraint>
                   <column-name>TITLE</column-name>
                </unique-constraint>
            </table>
            <sequence-generator name="ArticleSeq", sequenceName="ART_SEQ"/>
            <attributes>
                <id name="id">
                    <generated-value strategy="SEQUENCE" generator="ArticleSeq"/>
                </id>
                <basic name="title"/>
                <basic name="content"/>
                <version name="version">
                    <column name="VERS"/>
                </version>
                ...
            </attributes>
        </entity>
        <entity class="org.mag.pub.Company">
            <table name="COMP"/>
            <attributes>
                <id name="id">
                    <column name="CID"/>
                </id>
                <basic name="name"/>
                <basic name="revenue">
                    <column name="REV"/>
                </basic>
            </attributes>
        </entity>
        <entity class="org.mag.pub.Author">
            <table name="AUTH"/>
            <attributes>
                <id name="id">
                    <column name="AID" column-definition="INTEGER64"/>
                    <generated-value strategy="TABLE" generator="AuthorGen"/>
                    <table-generator name="AuthorGen" table="AUTH_GEN"
                        pk-column-name="PK" value-column-name="AID"/>
                </id>
                <basic name="firstName">
                    <column name="FNAME"/>
                </basic>
                <basic name="lastName">
                    <column name="LNAME"/>
                </basic>
                <version name="version">
                    <column name="VERS"/>
                </version>
                ...
            </attributes>
        </entity>
        <entity class="org.mag.subcribe.Contract">
            <table schema="CNTRCT"/>
            <inheritance strategy="JOINED"/>
            <discriminator-column name="CTYPE"/>
            <attributes>
                <basic name="terms">
                    <lob/>
                </basic>
                ...
            </attributes>
        </entity>
        <entity class="org.mag.subcribe.Subscription">
            <table name="SUB" schema="CNTRCT"/>
            <inheritance strategy="SINGLE_TABLE"/>
            <discriminator-value>1</discriminator-value>
            <discriminator-column name="KIND" discriminator-type="INTEGER"/>
            <attributes>
                <id name="id">
                    <generated-value strategy="IDENTITY"/>
                </id>
                <basic name="payment">
                    <column name="PAY"/>
                </basic>
                <basic name="startDate">
                    <column name="START"/>
                </basic>
                <version name="version">
                    <column name="VERS"/>
                </version>
                ...
            </attributes>
        </entity>
        <entity class="org.mag.subscribe.Subscription.LineItem">
            <table name="LINE_ITEM" schema="CNTRCT"/>
            <primary-key-join-column name="ID" referenced-column-name="PK"/>
            <attributes>
                <basic name="comments">
                    <column name="COMM"/>
                </basic>
                <basic name="price"/>
                <basic name="num"/>
                ...
            </attributes>
        </entity>
        <entity class="org.mag.subscribe.LifetimeSubscription" name="Lifetime">
            <discriminator-value>2</discriminator-value>
            <attributes>
                <basic name="eliteClub" fetch="LAZY">
                    <column name="ELITE"/>
                </basic>
                ...
            </attributes>
        </entity>
        <entity class="org.mag.subscribe.TrialSubscription" name="Trial">
            <discriminator-value>3</discriminator-value>
            <attributes>
                <basic name="endDate">
                    <column name="END"/>
                </basic>
                ...
            </attributes>
        </entity>
    </entity-mappings>

### 8.2. Secondary Tables

Sometimes a logical record is spread over multiple database tables. JPA 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 `table` attribute of each of the field's columns or join columns to the name of the secondary table.

2. Define the secondary table on the entity class declaration.

You define secondary tables with the `SecondaryTable` annotation. This annotation has all the properties of the `Table` annotation covered in Section 1, “ Table ” , plus a `pkJoinColumns` property.

The `pkJoinColumns` property is an array of `PrimaryKeyJoinColumn`s dictating how to join secondary table records to their owning primary table records. Each `PrimaryKeyJoinColumn` joins a secondary table column to a primary key column in the primary table. See Section 6.2, “ Joined ” above for coverage of `PrimaryKeyJoinColumn`'s properties.

The corresponding XML element is `secondary-table`. This element has all the attributes of the `table` element, but also accepts nested `primary-key-join-column` elements.

In the following example, we move the `Article.content` field we mapped in Section 8.1, “ Basic Mapping ” into a joined secondary table, like so:

Example 13.11. Secondary Table Field Mapping

    package org.mag;

    @Entity
    @Table(name="ART")
    @SecondaryTable(name="ART_DATA",
        pkJoinColumns=@PrimaryKeyJoinColumn(name="ART_ID", referencedColumnName="ID"))
    public class Article {

        @Id private long id;

        @Column(table="ART_DATA")
        private byte[] content;

        ...
    }

And in XML:

    <entity class="org.mag.Article">
        <table name="ART"/>
        <secondary-table name="ART_DATA">
            <primary-key-join-column name="ART_ID" referenced-column-name="ID"/>
        </secondary-table>
        <attributes>
            <id name="id"/>
            <basic name="content">
                <column table="ART_DATA"/>
            </basic>
            ...
        </attributes>
    </entity>

### 8.3. Embedded Mapping

Chapter 5, Metadata describes JPA's concept of embeddable 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 embeddable object as a foreign key, you map all the fields of the embeddable instance to columns in the owning field's table.

JPA defaults the embedded column names and descriptions to those of the embeddable class' field mappings. The `AttributeOverride` annotation overrides a basic embedded mapping. This annotation has the following properties:

- `String name`: The name of the embedded class' field being mapped to this class' table.

- `Column column`: The column defining the mapping of the embedded class' field to this class' table.

The corresponding XML element is `attribute-override`. It has a single `name` attribute to name the field being overridden, and a single `column` child element.

To declare multiple overrides, use the `AttributeOverrides` annotation, whose value is an array of `AttributeOverride` s. In XML, simply list multiple `attribute-override` elements in succession.

To override a many to one or one to one relationship, use the `AssociationOverride` annotation in place of `AttributeOverride`. `AssociationOverride` has the following properties:

- `String name`: The name of the embedded class' field being mapped to this class' table.

- `JoinColumn[] joinColumns`: The foreign key columns joining to the related record.

The corresponding XML element is `association-override`. It has a single `name` attribute to name the field being overridden, and one or more `join-column` child elements.

To declare multiple relation overrides, use the `AssociationOverrides` annotation, whose value is an array of `AssociationOverride` s. In XML, simply list multiple `association-override` elements in succession.

Example 13.12. Embedded Field Mapping

In this example, `Company` overrides the default mapping of `Address.street` and `Address.city`. All other embedded mappings are taken from the `Address` embeddable class.

    package org.mag.pub;

    @Entity
    @Table(name="COMP")
    public class Company {

        @Embedded
        @AttributeOverrides({
            @AttributeOverride(name="street", column=@Column(name="STRT")),
            @AttributeOverride(name="city", column=@Column(name="ACITY"))
        })
        private Address address;

        ...
    }

    @Entity
    @Table(name="AUTH")
    public class Author {

        // use all defaults from Address class mappings
        private Address address;

        ...
    }

    @Embeddable
    public class Address {

        private String street;
        private String city;
        @Column(columnDefinition="CHAR(2)")
        private String state;
        private String zip;
    }

The same metadata expressed in XML:

    <entity class="org.mag.pub.Company">
        <table name="COMP"/>
        <attributes>
            ...
            <embedded name="address">
                <attribute-override name="street">
                   <column name="STRT"/>
                </attribute-override>
                <attribute-override name="city">
                   <column name="ACITY"/>
                </attribute-override>
            </embedded>
        </attributes>
    </entity>
    <entity class="org.mag.pub.Author">
        <table name="AUTH"/>
        <attributes>
            <embedded name="address">
                <!-- use all defaults from Address -->
            </embedded>
        </attributes>
    </entity>
    <embeddable class="org.mag.pub.Address">
        <attributes>
            <basic name="street"/>
            <basic name="city"/>
            <basic name="state">
                <column column-definition="CHAR(2)"/>
            </basic>
            <basic name="zip"/>
        </attributes>
    </embeddable>

You can also use attribute overrides on an entity class to override mappings defined by its mapped superclass or table-per-class superclass. The example below re-maps the `Document.version` field to the `Contract` table's `CVERSION` column.

Example 13.13. Mapping Mapped Superclass Field

    @MappedSuperclass
    public abstract class Document {

        @Column(name="VERS")
        @Version private int version;

        ...
    }

    @Entity
    @Table(schema="CNTRCT")
    @Inheritance(strategy=InheritanceType.JOINED)
    @DiscriminatorColumn(name="CTYPE")
    @AttributeOverride(name="version", column=@Column(name="CVERSION"))
    public class Contract
        extends Document {
        ...
    }

The same metadata expressed in XML form:

    <mapped-superclass class="org.mag.subcribe.Document">
        <attributes>
            <version name="version">
                <column name="VERS">
            </version>
            ...
        </attributes>
    </mapped-superclass>
    <entity class="org.mag.subcribe.Contract">
        <table schema="CNTRCT"/>
        <inheritance strategy="JOINED"/>
        <discriminator-column name="CTYPE"/>
        <attribute-override name="version">
            <column name="CVERSION"/>
        </attribute-override>
        <attributes>
            ...
        </attributes>
    </entity>

### 8.4. Direct Relations

A direct relation is a non-embedded persistent field that holds a reference to another entity. many to one and one to one metadata field types are mapped as direct relations. 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 typically map a direct relation with `JoinColumn` annotations describing how the local foreign key columns join to the primary key columns of the related record. The `JoinColumn` annotation exposes the following properties:

- `String name`: The name of the foreign key column. Defaults to the relation field name, plus an underscore, plus the name of the referenced primary key column.

- `String referencedColumnName`: The name of the primary key column being joined to. If there is only one identity field in the related entity class, the join column name defaults to the name of the identity field's column.

- `boolean unique`: Whether this column is guaranteed to hold unique values for all rows. Defaults to false.

`JoinColumn` also has the same `nullable` , `insertable`, `updatable`, `columnDefinition`, and `table` properties as the `Column` annotation. See Section 3, “ Column ” for details on these properties.

The `join-column` element represents a join column in XML. Its attributes mirror the above annotation's properties:

- `name`

- `referenced-column-name`

- `unique`

- `nullable`

- `insertable`

- `updatable`

- `column-definition`

- `table`

When there are multiple columns involved in the join, as when a `LineItem` references a `Magazine` in our model, the `JoinColumns` annotation allows you to specify an array of `JoinColumn` values. In XML, simply list multiple `join-column` elements.

### Note

OpenJPA supports many non-standard joins. See Section 6, “ Non-Standard Joins ” in the Reference Guide for details.

Example 13.14. Direct Relation Field Mapping

    package org.mag;

    @Table(name="AUTH")
    public class Magazine {

        @Column(length=9)
        @Id private String isbn;
        @Id private String title;

        @OneToOne
        @JoinColumn(name="COVER_ID" referencedColumnName="ID")
        private Article coverArticle;

        @ManyToOne
        @JoinColumn(name="PUB_ID" referencedColumnName="CID")
        private Company publisher;

        ...
    }

    @Table(name="ART")
    public class Article {

        @Id private long id;

        ...
    }

    package org.mag.pub;

    @Table(name="COMP")
    public class Company {

        @Column(name="CID")
        @Id private long id;

        ...
    }

    package org.mag.subscribe;

    public class Subscription {
        ...

        @Table(name="LINE_ITEM", schema="CNTRCT")
        public static class LineItem
            extends Contract {

            @ManyToOne
            @JoinColumns({
                @JoinColumn(name="MAG_ISBN" referencedColumnName="ISBN"),
                @JoinColumn(name="MAG_TITLE" referencedColumnName="TITLE")
            })
            private Magazine magazine;

            ...
        }
    }

The same metadata expressed in XML form:

    <entity class="org.mag.Magazine">
        <table name="MAG"/>
        <id-class="org.mag.Magazine.MagazineId"/>
        <attributes>
            <id name="isbn">
                <column length="9"/>
            </id>
            <id name="title"/>
            <one-to-one name="coverArticle">
                <join-column name="COVER_ID" referenced-column-name="ID"/>
            </one-to-one>
            <many-to-one name="publisher">
                <join-column name="PUB_IC" referenced-column-name="CID"/>
            </many-to-one>
            ...
        </attributes>
    </entity>
    <entity class="org.mag.Article">
        <table name="ART"/>
        <attributes>
            <id name="id"/>
            ...
        </attributes>
    </entity>
    <entity class="org.mag.pub.Company">
        <table name="COMP"/>
        <attributes>
            <id name="id">
                <column name="CID"/>
            </id>
            ...
        </attributes>
    </entity>
    <entity class="org.mag.subscribe.Subscription.LineItem">
        <table name="LINE_ITEM" schema="CNTRCT"/>
        <primary-key-join-column name="ID" referenced-column-name="PK"/>
        <attributes>
            <many-to-one name="magazine">
                <join-column name="MAG_ISBN" referenced-column-name="ISBN"/>
                <join-column name="MAG_TITLE" referenced-column-name="TITLE"/>
            </many-to-one>
            ...
        </attributes>
    </entity>

When the entities in a one to one relation join on shared primary key values rather than separate foreign key columns, use the `PrimaryKeyJoinColumn(s)` annotation or `primary-key-join-column` elements in place of `JoinColumn(s)` / `join-column` elements.

### 8.5. Join Table

A join table consists of two foreign keys. Each row of a join table associates two objects together. JPA uses join tables to represent collections of entity objects: one foreign key refers back to the collection's owner, and the other refers to a collection element.

one to many and many to many metadata field types can map to join tables. Several fields in our model use join table mappings, including `Magazine.articles` and `Article.authors`.

You define join tables with the `JoinTable` annotation. This annotation has the following properties:

- `String name`: Table name. If not given, the name of the table defaults to the name of the owning entity's table, plus an underscore, plus the name of the related entity's table.

- `String catalog`: Table catalog.

- `String schema`: Table schema.

- `JoinColumn[] joinColumns`: Array of `JoinColumn` showing how to associate join table records with the owning row in the primary table. This property mirrors the `pkJoinColumns` property of the `SecondaryTable` annotation in functionality. See Section 8.2, “ Secondary Tables ” to refresh your memory on secondary tables.

  If this is a bidirectional relation (see Section 2.10.1, “ Bidirectional Relations ” ), the name of a join column defaults to the inverse field name, plus an underscore, plus the referenced primary key column name. Otherwise, the join column name defaults to the field's owning entity name, plus an underscore, plus the referenced primary key column name.

- `JoinColumn[] inverseJoinColumns`: Array of `JoinColumns` showing how to associate join table records with the records that form the elements of the collection. These join columns are used just like the join columns for direct relations, and they have the same naming defaults. Read Section 8.4, “ Direct Relations ” for a review of direct relation mapping.

`join-table` is the corresponding XML element. It has the same attributes as the `table` element, but includes the ability to nest `join-column` and `inverse-join-column` elements as children. We have seen `join-column` elements already; `inverse-join-column` elements have the same attributes.

Here are the join table mappings for the diagram above.

Example 13.15. Join Table Mapping

    package org.mag;

    @Entity
    @Table(name="MAG")
    public class Magazine {

        @Column(length=9)
        @Id private String isbn;
        @Id private String title;

        @OneToMany(...)
        @OrderBy
        @JoinTable(name="MAG_ARTS",
            joinColumns={
                @JoinColumn(name="MAG_ISBN", referencedColumnName="ISBN"),
                @JoinColumn(name="MAG_TITLE", referencedColumnName="TITLE")
            },
            inverseJoinColumns=@JoinColumn(name="ART_ID", referencedColumnName="ID"))
        private Collection<Article> articles;

        ...
    }

    @Entity
    @Table(name="ART")
    public class Article {

        @Id private long id;

        @ManyToMany(cascade=CascadeType.PERSIST)
        @OrderBy("lastName, firstName")
        @JoinTable(name="ART_AUTHS",
            joinColumns=@JoinColumn(name="ART_ID", referencedColumnName="ID"),
            inverseJoinColumns=@JoinColumn(name="AUTH_ID", referencedColumnName="AID"))
        private Collection<Author> authors;

        ...
    }

    package org.mag.pub;

    @Entity
    @Table(name="AUTH")
    public class Author {

        @Column(name="AID", columnDefinition="INTEGER64")
        @Id private long id;

        ...
    }

The same metadata expressed in XML:

    <entity class="org.mag.Magazine">
        <table name="MAG"/>
        <attributes>
            <id name="isbn">
                <column length="9"/>
            </id>
            <id name="title"/>
            <one-to-many name="articles">
                <order-by/>
                <join-table name="MAG_ARTS">
                    <join-column name="MAG_ISBN" referenced-column-name="ISBN"/>
                    <join-column name="MAG_TITLE" referenced-column-name="TITLE"/>
                    <inverse-join-column name="ART_ID" referenced-column-name="ID"/>
                </join-table>
            </one-to-many>
            ...
        </attributes>
    </entity>
    <entity class="org.mag.Article">
        <table name="ART"/>
            <attributes>
                <id name="id"/>
                <many-to-many name="authors">
                    <order-by>lastName, firstName</order-by>
                    <join-table name="ART_AUTHS">
                        <join-column name="ART_ID" referenced-column-name="ID"/>
                        <inverse-join-column name="AUTH_ID" referenced-column-name="AID"/>
                    </join-table>
                <cascade>
                    <cascade-persist/>
                </cascade>
                </many-to-many>
                ...
           </attributes>
    </entity>
    <entity class="org.mag.pub.Author">
        <table name="AUTH"/>
        <attributes>
            <id name="id">
                <column name="AID" column-definition="INTEGER64"/>
            </id>
            ...
        </attributes>
    </entity>

### 8.6. Bidirectional Mapping

Section 2.10.1, “ Bidirectional Relations ” introduced bidirectional relations. To map a bidirectional relation, you map one field normally using the annotations we have covered throughout this chapter. Then you use the `mappedBy` property of the other field's metadata annotation or the corresponding `mapped-by` XML attribute to refer to the mapped field. Look for this pattern in these bidirectional relations as you peruse the complete mappings below:

- `Magazine.publisher` and `Company.mags`.

- `Article.authors` and `Author.articles`.

### 8.7. Map Mapping

All map fields in JPA are modeled on either one to many or many to many associations. The map key is always derived from an associated entity's field. Thus map fields use the same mappings as any one to many or many to many fields, namely dedicated join tables or bidirectional relations. The only additions are the `MapKey` annotation and `map-key` element to declare the key field. We covered these additions in Section 2.14, “ Map Key ”.

The example below maps `Subscription`'s map of `LineItem`s to the `SUB_ITEMS` join table. The key for each map entry is the `LineItem`'s `num` field value.

Example 13.16. Join Table Map Mapping

    package org.mag.subscribe;

    @Entity
    @Table(name="SUB", schema="CNTRCT")
    public class Subscription {

        @OneToMany(cascade={CascadeType.PERSIST,CascadeType.REMOVE})
        @MapKey(name="num")
        @JoinTable(name="SUB_ITEMS", schema="CNTRCT",
            joinColumns=@JoinColumn(name="SUB_ID"),
            inverseJoinColumns=@JoinColumn(name="ITEM_ID"))
        private Map<Long,LineItem> items;

        ...

        @Entity
        @Table(name="LINE_ITEM", schema="CNTRCT")
        public static class LineItem
            extends Contract {

            private long num;

            ...
        }
    }

The same metadata expressed in XML:

    <entity class="org.mag.subscribe.Subscription">
        <table name="SUB" schema="CNTRCT"/>
        <attributes>
            ...
            <one-to-many name="items">
                <map-key name="num">
                <join-table name="SUB_ITEMS" schema="CNTRCT">
                    <join-column name="SUB_ID"/>
                    <inverse-join-column name="ITEM_ID"/>
                </join-table>
                <cascade>
                    <cascade-persist/>
                    <cascade-remove/>
                </cascade>
            </one-to-many>
            ...
        </attributes>
    </entity>
    <entity class="org.mag.subscribe.Subscription.LineItem">
        <table name="LINE_ITEM" schema="CNTRCT"/>
        <attributes>
            ...
            <basic name="num"/>
            ...
        </attributes>
    </entity>

Prev | Up | Next
7. Discriminator | Home | 9. The Complete Mappings