526 lines
16 KiB
XML
526 lines
16 KiB
XML
|
<chapter id="associations">
|
||
|
|
||
|
<title>Association Mappings</title>
|
||
|
|
||
|
<sect1 id="assoc-intro" revision="1">
|
||
|
<title>Introduction</title>
|
||
|
|
||
|
<para>
|
||
|
Association mappings are the often most difficult thing to get right. In
|
||
|
this section we'll go through the canonical cases one by one, starting
|
||
|
with unidirectional mappings, and then considering the bidirectional cases.
|
||
|
We'll use <literal>Person</literal> and <literal>Address</literal> in all
|
||
|
the examples.
|
||
|
</para>
|
||
|
|
||
|
<para>
|
||
|
We'll classify associations by whether or not they map to an intervening
|
||
|
join table, and by multiplicity.
|
||
|
</para>
|
||
|
|
||
|
<para>
|
||
|
Nullable foreign keys are not considered good practice in traditional data
|
||
|
modelling, so all our examples use not null foreign keys. This is not a
|
||
|
requirement of Hibernate, and the mappings will all work if you drop the
|
||
|
nullability constraints.
|
||
|
</para>
|
||
|
|
||
|
</sect1>
|
||
|
|
||
|
<sect1 id="assoc-unidirectional" revision="1">
|
||
|
<title>Unidirectional associations</title>
|
||
|
|
||
|
<sect2 id="assoc-unidirectional-m21">
|
||
|
<title>many to one</title>
|
||
|
|
||
|
<para>
|
||
|
A <emphasis>unidirectional many-to-one association</emphasis> is the most
|
||
|
common kind of unidirectional association.
|
||
|
</para>
|
||
|
|
||
|
<programlisting><![CDATA[<class name="Person">
|
||
|
<id name="id" column="personId">
|
||
|
<generator class="native"/>
|
||
|
</id>
|
||
|
<many-to-one name="address"
|
||
|
column="addressId"
|
||
|
not-null="true"/>
|
||
|
</class>
|
||
|
|
||
|
<class name="Address">
|
||
|
<id name="id" column="addressId">
|
||
|
<generator class="native"/>
|
||
|
</id>
|
||
|
</class>]]></programlisting>
|
||
|
<programlisting><![CDATA[
|
||
|
create table Person ( personId bigint not null primary key, addressId bigint not null )
|
||
|
create table Address ( addressId bigint not null primary key )
|
||
|
]]></programlisting>
|
||
|
|
||
|
</sect2>
|
||
|
|
||
|
<sect2 id="assoc-unidirectional-121">
|
||
|
<title>one to one</title>
|
||
|
|
||
|
<para>
|
||
|
A <emphasis>unidirectional one-to-one association on a foreign key</emphasis>
|
||
|
is almost identical. The only difference is the column unique constraint.
|
||
|
</para>
|
||
|
|
||
|
<programlisting><![CDATA[<class name="Person">
|
||
|
<id name="id" column="personId">
|
||
|
<generator class="native"/>
|
||
|
</id>
|
||
|
<many-to-one name="address"
|
||
|
column="addressId"
|
||
|
unique="true"
|
||
|
not-null="true"/>
|
||
|
</class>
|
||
|
|
||
|
<class name="Address">
|
||
|
<id name="id" column="addressId">
|
||
|
<generator class="native"/>
|
||
|
</id>
|
||
|
</class>]]></programlisting>
|
||
|
<programlisting><![CDATA[
|
||
|
create table Person ( personId bigint not null primary key, addressId bigint not null unique )
|
||
|
create table Address ( addressId bigint not null primary key )
|
||
|
]]></programlisting>
|
||
|
|
||
|
<para>
|
||
|
A <emphasis>unidirectional one-to-one association on a primary key</emphasis>
|
||
|
usually uses a special id generator. (Notice that we've reversed the direction
|
||
|
of the association in this example.)
|
||
|
</para>
|
||
|
|
||
|
<programlisting><![CDATA[<class name="Person">
|
||
|
<id name="id" column="personId">
|
||
|
<generator class="native"/>
|
||
|
</id>
|
||
|
</class>
|
||
|
|
||
|
<class name="Address">
|
||
|
<id name="id" column="personId">
|
||
|
<generator class="foreign">
|
||
|
<param name="property">person</param>
|
||
|
</generator>
|
||
|
</id>
|
||
|
<one-to-one name="person" constrained="true"/>
|
||
|
</class>]]></programlisting>
|
||
|
<programlisting><![CDATA[
|
||
|
create table Person ( personId bigint not null primary key )
|
||
|
create table Address ( personId bigint not null primary key )
|
||
|
]]></programlisting>
|
||
|
|
||
|
</sect2>
|
||
|
|
||
|
<sect2 id="assoc-unidirectional-12m">
|
||
|
<title>one to many</title>
|
||
|
|
||
|
<para>
|
||
|
A <emphasis>unidirectional one-to-many association on a foreign key</emphasis>
|
||
|
is a very unusual case, and is not really recommended.
|
||
|
</para>
|
||
|
|
||
|
<programlisting><![CDATA[<class name="Person">
|
||
|
<id name="id" column="personId">
|
||
|
<generator class="native"/>
|
||
|
</id>
|
||
|
<set name="addresses">
|
||
|
<key column="personId"
|
||
|
not-null="true"/>
|
||
|
<one-to-many class="Address"/>
|
||
|
</set>
|
||
|
</class>
|
||
|
|
||
|
<class name="Address">
|
||
|
<id name="id" column="addressId">
|
||
|
<generator class="native"/>
|
||
|
</id>
|
||
|
</class>]]></programlisting>
|
||
|
<programlisting><![CDATA[
|
||
|
create table Person ( personId bigint not null primary key )
|
||
|
create table Address ( addressId bigint not null primary key, personId bigint not null )
|
||
|
]]></programlisting>
|
||
|
|
||
|
<para>
|
||
|
We think it's better to use a join table for this kind of association.
|
||
|
</para>
|
||
|
|
||
|
</sect2>
|
||
|
|
||
|
</sect1>
|
||
|
|
||
|
<sect1 id="assoc-unidirectional-join" revision="1">
|
||
|
<title>Unidirectional associations with join tables</title>
|
||
|
|
||
|
<sect2 id="assoc-unidirectional-join-12m">
|
||
|
<title>one to many</title>
|
||
|
|
||
|
<para>
|
||
|
A <emphasis>unidirectional one-to-many association on a join table</emphasis>
|
||
|
is much preferred. Notice that by specifying <literal>unique="true"</literal>,
|
||
|
we have changed the multiplicity from many-to-many to one-to-many.
|
||
|
</para>
|
||
|
|
||
|
<programlisting><![CDATA[<class name="Person">
|
||
|
<id name="id" column="personId">
|
||
|
<generator class="native"/>
|
||
|
</id>
|
||
|
<set name="addresses" table="PersonAddress">
|
||
|
<key column="personId"/>
|
||
|
<many-to-many column="addressId"
|
||
|
unique="true"
|
||
|
class="Address"/>
|
||
|
</set>
|
||
|
</class>
|
||
|
|
||
|
<class name="Address">
|
||
|
<id name="id" column="addressId">
|
||
|
<generator class="native"/>
|
||
|
</id>
|
||
|
</class>]]></programlisting>
|
||
|
<programlisting><![CDATA[
|
||
|
create table Person ( personId bigint not null primary key )
|
||
|
create table PersonAddress ( personId not null, addressId bigint not null primary key )
|
||
|
create table Address ( addressId bigint not null primary key )
|
||
|
]]></programlisting>
|
||
|
|
||
|
</sect2>
|
||
|
|
||
|
<sect2 id="assoc-unidirectional-join-m21">
|
||
|
<title>many to one</title>
|
||
|
|
||
|
<para>
|
||
|
A <emphasis>unidirectional many-to-one association on a join table</emphasis>
|
||
|
is quite common when the association is optional.
|
||
|
</para>
|
||
|
|
||
|
<programlisting><![CDATA[<class name="Person">
|
||
|
<id name="id" column="personId">
|
||
|
<generator class="native"/>
|
||
|
</id>
|
||
|
<join table="PersonAddress"
|
||
|
optional="true">
|
||
|
<key column="personId" unique="true"/>
|
||
|
<many-to-one name="address"
|
||
|
column="addressId"
|
||
|
not-null="true"/>
|
||
|
</join>
|
||
|
</class>
|
||
|
|
||
|
<class name="Address">
|
||
|
<id name="id" column="addressId">
|
||
|
<generator class="native"/>
|
||
|
</id>
|
||
|
</class>]]></programlisting>
|
||
|
<programlisting><![CDATA[
|
||
|
create table Person ( personId bigint not null primary key )
|
||
|
create table PersonAddress ( personId bigint not null primary key, addressId bigint not null )
|
||
|
create table Address ( addressId bigint not null primary key )
|
||
|
]]></programlisting>
|
||
|
|
||
|
</sect2>
|
||
|
|
||
|
<sect2 id="assoc-unidirectional-join-121">
|
||
|
<title>one to one</title>
|
||
|
|
||
|
<para>
|
||
|
A <emphasis>unidirectional one-to-one association on a join table</emphasis>
|
||
|
is extremely unusual, but possible.
|
||
|
</para>
|
||
|
|
||
|
<programlisting><![CDATA[<class name="Person">
|
||
|
<id name="id" column="personId">
|
||
|
<generator class="native"/>
|
||
|
</id>
|
||
|
<join table="PersonAddress"
|
||
|
optional="true">
|
||
|
<key column="personId"
|
||
|
unique="true"/>
|
||
|
<many-to-one name="address"
|
||
|
column="addressId"
|
||
|
not-null="true"
|
||
|
unique="true"/>
|
||
|
</join>
|
||
|
</class>
|
||
|
|
||
|
<class name="Address">
|
||
|
<id name="id" column="addressId">
|
||
|
<generator class="native"/>
|
||
|
</id>
|
||
|
</class>]]></programlisting>
|
||
|
<programlisting><![CDATA[
|
||
|
create table Person ( personId bigint not null primary key )
|
||
|
create table PersonAddress ( personId bigint not null primary key, addressId bigint not null unique )
|
||
|
create table Address ( addressId bigint not null primary key )
|
||
|
]]></programlisting>
|
||
|
|
||
|
</sect2>
|
||
|
|
||
|
<sect2 id="assoc-unidirectional-join-m2m">
|
||
|
<title>many to many</title>
|
||
|
|
||
|
<para>
|
||
|
Finally, we have a <emphasis>unidirectional many-to-many association</emphasis>.
|
||
|
</para>
|
||
|
|
||
|
<programlisting><![CDATA[<class name="Person">
|
||
|
<id name="id" column="personId">
|
||
|
<generator class="native"/>
|
||
|
</id>
|
||
|
<set name="addresses" table="PersonAddress">
|
||
|
<key column="personId"/>
|
||
|
<many-to-many column="addressId"
|
||
|
class="Address"/>
|
||
|
</set>
|
||
|
</class>
|
||
|
|
||
|
<class name="Address">
|
||
|
<id name="id" column="addressId">
|
||
|
<generator class="native"/>
|
||
|
</id>
|
||
|
</class>]]></programlisting>
|
||
|
<programlisting><![CDATA[
|
||
|
create table Person ( personId bigint not null primary key )
|
||
|
create table PersonAddress ( personId bigint not null, addressId bigint not null, primary key (personId, addressId) )
|
||
|
create table Address ( addressId bigint not null primary key )
|
||
|
]]></programlisting>
|
||
|
|
||
|
</sect2>
|
||
|
|
||
|
</sect1>
|
||
|
|
||
|
<sect1 id="assoc-bidirectional" revision="1">
|
||
|
<title>Bidirectional associations</title>
|
||
|
|
||
|
<sect2 id="assoc-bidirectional-m21">
|
||
|
<title>one to many / many to one</title>
|
||
|
|
||
|
<para>
|
||
|
A <emphasis>bidirectional many-to-one association</emphasis> is the
|
||
|
most common kind of association. (This is the standard parent/child
|
||
|
relationship.)
|
||
|
</para>
|
||
|
|
||
|
<programlisting><![CDATA[<class name="Person">
|
||
|
<id name="id" column="personId">
|
||
|
<generator class="native"/>
|
||
|
</id>
|
||
|
<many-to-one name="address"
|
||
|
column="addressId"
|
||
|
not-null="true"/>
|
||
|
</class>
|
||
|
|
||
|
<class name="Address">
|
||
|
<id name="id" column="addressId">
|
||
|
<generator class="native"/>
|
||
|
</id>
|
||
|
<set name="people" inverse="true">
|
||
|
<key column="addressId"/>
|
||
|
<one-to-many class="Person"/>
|
||
|
</set>
|
||
|
</class>]]></programlisting>
|
||
|
|
||
|
<programlisting><![CDATA[
|
||
|
create table Person ( personId bigint not null primary key, addressId bigint not null )
|
||
|
create table Address ( addressId bigint not null primary key )
|
||
|
]]></programlisting>
|
||
|
|
||
|
</sect2>
|
||
|
|
||
|
<sect2 id="assoc-bidirectional-121">
|
||
|
<title>one to one</title>
|
||
|
|
||
|
<para>
|
||
|
A <emphasis>bidirectional one-to-one association on a foreign key</emphasis>
|
||
|
is quite common.
|
||
|
</para>
|
||
|
|
||
|
<programlisting><![CDATA[<class name="Person">
|
||
|
<id name="id" column="personId">
|
||
|
<generator class="native"/>
|
||
|
</id>
|
||
|
<many-to-one name="address"
|
||
|
column="addressId"
|
||
|
unique="true"
|
||
|
not-null="true"/>
|
||
|
</class>
|
||
|
|
||
|
<class name="Address">
|
||
|
<id name="id" column="addressId">
|
||
|
<generator class="native"/>
|
||
|
</id>
|
||
|
<one-to-one name="person"
|
||
|
property-ref="address"/>
|
||
|
</class>]]></programlisting>
|
||
|
<programlisting><![CDATA[
|
||
|
create table Person ( personId bigint not null primary key, addressId bigint not null unique )
|
||
|
create table Address ( addressId bigint not null primary key )
|
||
|
]]></programlisting>
|
||
|
|
||
|
<para>
|
||
|
A <emphasis>bidirectional one-to-one association on a primary key</emphasis>
|
||
|
uses the special id generator.
|
||
|
</para>
|
||
|
|
||
|
<programlisting><![CDATA[<class name="Person">
|
||
|
<id name="id" column="personId">
|
||
|
<generator class="native"/>
|
||
|
</id>
|
||
|
<one-to-one name="address"/>
|
||
|
</class>
|
||
|
|
||
|
<class name="Address">
|
||
|
<id name="id" column="personId">
|
||
|
<generator class="foreign">
|
||
|
<param name="property">person</param>
|
||
|
</generator>
|
||
|
</id>
|
||
|
<one-to-one name="person"
|
||
|
constrained="true"/>
|
||
|
</class>]]></programlisting>
|
||
|
<programlisting><![CDATA[
|
||
|
create table Person ( personId bigint not null primary key )
|
||
|
create table Address ( personId bigint not null primary key )
|
||
|
]]></programlisting>
|
||
|
|
||
|
</sect2>
|
||
|
|
||
|
</sect1>
|
||
|
|
||
|
<sect1 id="assoc-bidirectional-join" revision="1">
|
||
|
<title>Bidirectional associations with join tables</title>
|
||
|
|
||
|
<sect2 id="assoc-bidirectional-join-12m">
|
||
|
<title>one to many / many to one</title>
|
||
|
|
||
|
<para>
|
||
|
A <emphasis>bidirectional one-to-many association on a join table</emphasis>.
|
||
|
Note that the <literal>inverse="true"</literal> can go on either end of the
|
||
|
association, on the collection, or on the join.
|
||
|
</para>
|
||
|
|
||
|
<programlisting><![CDATA[<class name="Person">
|
||
|
<id name="id" column="personId">
|
||
|
<generator class="native"/>
|
||
|
</id>
|
||
|
<set name="addresses"
|
||
|
table="PersonAddress">
|
||
|
<key column="personId"/>
|
||
|
<many-to-many column="addressId"
|
||
|
unique="true"
|
||
|
class="Address"/>
|
||
|
</set>
|
||
|
</class>
|
||
|
|
||
|
<class name="Address">
|
||
|
<id name="id" column="addressId">
|
||
|
<generator class="native"/>
|
||
|
</id>
|
||
|
<join table="PersonAddress"
|
||
|
inverse="true"
|
||
|
optional="true">
|
||
|
<key column="addressId"/>
|
||
|
<many-to-one name="person"
|
||
|
column="personId"
|
||
|
not-null="true"/>
|
||
|
</join>
|
||
|
</class>]]></programlisting>
|
||
|
<programlisting><![CDATA[
|
||
|
create table Person ( personId bigint not null primary key )
|
||
|
create table PersonAddress ( personId bigint not null, addressId bigint not null primary key )
|
||
|
create table Address ( addressId bigint not null primary key )
|
||
|
]]></programlisting>
|
||
|
|
||
|
</sect2>
|
||
|
|
||
|
<sect2 id="assoc-bidirectional-join-121">
|
||
|
<title>one to one</title>
|
||
|
|
||
|
<para>
|
||
|
A <emphasis>bidirectional one-to-one association on a join table</emphasis>
|
||
|
is extremely unusual, but possible.
|
||
|
</para>
|
||
|
|
||
|
<programlisting><![CDATA[<class name="Person">
|
||
|
<id name="id" column="personId">
|
||
|
<generator class="native"/>
|
||
|
</id>
|
||
|
<join table="PersonAddress"
|
||
|
optional="true">
|
||
|
<key column="personId"
|
||
|
unique="true"/>
|
||
|
<many-to-one name="address"
|
||
|
column="addressId"
|
||
|
not-null="true"
|
||
|
unique="true"/>
|
||
|
</join>
|
||
|
</class>
|
||
|
|
||
|
<class name="Address">
|
||
|
<id name="id" column="addressId">
|
||
|
<generator class="native"/>
|
||
|
</id>
|
||
|
<join table="PersonAddress"
|
||
|
optional="true"
|
||
|
inverse="true">
|
||
|
<key column="addressId"
|
||
|
unique="true"/>
|
||
|
<many-to-one name="address"
|
||
|
column="personId"
|
||
|
not-null="true"
|
||
|
unique="true"/>
|
||
|
</join>
|
||
|
</class>]]></programlisting>
|
||
|
<programlisting><![CDATA[
|
||
|
create table Person ( personId bigint not null primary key )
|
||
|
create table PersonAddress ( personId bigint not null primary key, addressId bigint not null unique )
|
||
|
create table Address ( addressId bigint not null primary key )
|
||
|
]]></programlisting>
|
||
|
|
||
|
</sect2>
|
||
|
|
||
|
<sect2 id="assoc-bidirectional-join-m2m">
|
||
|
<title>many to many</title>
|
||
|
|
||
|
<para>
|
||
|
Finally, we have a <emphasis>bidirectional many-to-many association</emphasis>.
|
||
|
</para>
|
||
|
|
||
|
<programlisting><![CDATA[<class name="Person">
|
||
|
<id name="id" column="personId">
|
||
|
<generator class="native"/>
|
||
|
</id>
|
||
|
<set name="addresses">
|
||
|
<key column="personId"/>
|
||
|
<many-to-many column="addressId"
|
||
|
class="Address"/>
|
||
|
</set>
|
||
|
</class>
|
||
|
|
||
|
<class name="Address">
|
||
|
<id name="id" column="addressId">
|
||
|
<generator class="native"/>
|
||
|
</id>
|
||
|
<set name="people" inverse="true">
|
||
|
<key column="addressId"/>
|
||
|
<many-to-many column="personId"
|
||
|
class="Person"/>
|
||
|
</set>
|
||
|
</class>]]></programlisting>
|
||
|
|
||
|
<programlisting><![CDATA[
|
||
|
create table Person ( personId bigint not null primary key )
|
||
|
create table PersonAddress ( personId bigint not null, addressId bigint not null, primary key (personId, addressId) )
|
||
|
create table Address ( addressId bigint not null primary key )
|
||
|
]]></programlisting>
|
||
|
|
||
|
</sect2>
|
||
|
|
||
|
</sect1>
|
||
|
|
||
|
|
||
|
</chapter>
|
||
|
|