Chapter 7. Association Mappings
Chapter 7. Association Mappings
Prev
Prev
Next
Next
Chapter 7. Association Mappings
Chapter 7. Association Mappings
7.1. Introduction
7.1. Introduction
Association mappings are the often most difficult thing to get right. In
Association mappings are the often most difficult thing to get right. In this section we'll
this section we'll
go through the canonical cases one by one, starting with unidirectional mappings, and
go through the canonical cases one by one, starting with unidirectional mappings, and
then considering the
then considering the bidirectional cases. W
bidirectional cases. We'll use
e'll use
PersonPersonand
and
AddressAddressin all the
in all the
examples.
examples.
W
We'll classify associations by whether or not they map to an
e'll classify associations by whether or not they map to an intervening join table, and by
intervening join table, and by
multiplicity.
multiplicity.
Nullable foreign keys are not considered
Nullable foreign keys are not considered good practice in traditional data modelling, so
good practice in traditional data modelling, so
all our examples use not
all our examples use not null foreign keys. This is not a requirement of Hibernate, and
null foreign keys. This is not a requirement of Hibernate, and the
the
mappings will all work if you drop the nullability constraints.
mappings will all work if you drop the nullability constraints.
7.2. Unidirectional associations
7.2. Unidirectional associations
7.2.1. many to one
7.2.1. many to one
A
A unidirectional many-to-one association
unidirectional many-to-one association is the most common kind of unidirectional
is the most common kind of unidirectional
association.
association.
<class name="Person"> <class name="Person">
<id name="id" column="personId"> <id name="id" column="personId"> <generator class="native"/> <generator class="native"/> </id> </id> <many-to-one name="address" <many-to-one name="address" column="addressId" column="addressId" not-null="true"/> not-null="true"/> </class> </class> <class name="Address"> <class name="Address">
<id name="id" column="addressId"> <id name="id" column="addressId">
<generator class="native"/> <generator class="native"/> </id> </id> </class> </class>
create table Person ( personId bigint not null primary key, addressId create table Person ( personId bigint not null primary key, addressId bigint not null )
bigint not null )
create table Address ( addressId bigint not null primary key ) create table Address ( addressId bigint not null primary key )
7.2.2. one to one
7.2.2. one to one
A
A unidirectional one-to-one association on a foreign key
unidirectional one-to-one association on a foreign key is almost identical. The only
is almost identical. The only
difference is the column unique constraint.
difference is the column unique constraint.
<class name="Person"><class name="Person">
<id name="id" column="personId"> <id name="id" column="personId"> <generator class="native"/> <generator class="native"/> </id> </id> <many-to-one name="address" <many-to-one name="address" column="addressId" column="addressId" unique="true" unique="true" not-null="true"/> not-null="true"/> </class> </class> <class name="Address"> <class name="Address">
<id name="id" column="addressId"> <id name="id" column="addressId">
<generator class="native"/> <generator class="native"/> </id> </id> </class> </class>
create table Person ( personId bigint not null primary key, addressId create table Person ( personId bigint not null primary key, addressId bigint not null unique )
bigint not null unique )
create table Address ( addressId bigint not null primary key ) create table Address ( addressId bigint not null primary key )
A
A unidirectional one-to-one association on a primary key
unidirectional one-to-one association on a primary key usually uses a special id
usually uses a special id
generator. (Notice that we've reversed the direction of the association in this example.)
generator. (Notice that we've reversed the direction of the association in this example.)
<class name="Person"><class name="Person">
<id name="id" column="personId"> <id name="id" column="personId"> <generator class="native"/> <generator class="native"/> </id> </id> </class> </class> <class name="Address"> <class name="Address">
<id name="id" column="personId"> <id name="id" column="personId"> <generator class="foreign"> <generator class="foreign"> <param name="property">person</param> <param name="property">person</param> </generator> </generator> </id> </id>
<one-to-one name="person" constrained="true"/> <one-to-one name="person" constrained="true"/> </class>
</class>
create table Person ( personId bigint not null primary key ) create table Person ( personId bigint not null primary key ) create table Address ( personId bigint not null primary key ) create table Address ( personId bigint not null primary key )
7.2.3. one to many
7.2.3. one to many
A
A unidirectional one-to-many association on a
unidirectional one-to-many association on a foreign key
foreign key is a very unusual case, and
is a very unusual case, and is
is
not really recommended.
not really recommended.
<class name="Person"> <class name="Person"><id name="id" column="personId"> <id name="id" column="personId"> <generator class="native"/> <generator class="native"/> </id> </id> <set name="addresses"> <set name="addresses">
<key column="personId" <key column="personId" not-null="true"/> not-null="true"/> <one-to-many class="Address"/> <one-to-many class="Address"/> </set> </set> </class> </class> <class name="Address"> <class name="Address">
<id name="id" column="addressId"> <id name="id" column="addressId">
<generator class="native"/> <generator class="native"/> </id> </id> </class> </class>
create table Person ( personId bigint not null primary key ) create table Person ( personId bigint not null primary key )
create table Address ( addressId bigint not null primary key, personId create table Address ( addressId bigint not null primary key, personId bigint not null )
bigint not null )
We think it's better to use a join table for this kind of association.
We think it's better to use a join table for this kind of association.
7.3. Unidirectional associations with join tables
7.3. Unidirectional associations with join tables
7.3.1. one to many
7.3.1. one to many
A
A unidirectional one-to-many association on a join table
unidirectional one-to-many association on a join table is much preferred. Notice that
is much preferred. Notice that
by specifying
by specifying
unique="true"unique="true", we have ch
, we have changed the multiplicity from many-to-many to
anged the multiplicity from many-to-many to
one-to-many.
one-to-many.
<class name="Person"> <class name="Person">
<id name="id" column="personId"> <id name="id" column="personId"> <generator class="native"/> <generator class="native"/> </id>
</id>
<set name="addresses" table="PersonAddress"> <set name="addresses" table="PersonAddress">
<key column="personId"/> <key column="personId"/> <many-to-many column="addressId" <many-to-many column="addressId" unique="true" unique="true" class="Address"/> class="Address"/> </set> </set> </class> </class> <class name="Address"> <class name="Address">
<id name="id" column="addressId"> <id name="id" column="addressId">
<generator class="native"/> <generator class="native"/> </id> </id> </class> </class>
create table Person ( personId bigint not null primary key ) create table Person ( personId bigint not null primary key )
create table PersonAddress ( personId not null, addressId bigint not create table PersonAddress ( personId not null, addressId bigint not null primary key )
null primary key )
create table Address ( addressId bigint not null primary key ) create table Address ( addressId bigint not null primary key )
7.3.2. many to one
7.3.2. many to one
A
A unidirectional many-to-one association on a join table
unidirectional many-to-one association on a join table is quite common when the
is quite common when the
association is optional.
association is optional.
<class name="Person"> <class name="Person"><id name="id" column="personId"> <id name="id" column="personId"> <generator class="native"/> <generator class="native"/> </id> </id> <join table="PersonAddress" <join table="PersonAddress" optional="true"> optional="true">
<key column="personId" unique="true"/> <key column="personId" unique="true"/> <many-to-one name="address" <many-to-one name="address" column="addressId" column="addressId" not-null="true"/> not-null="true"/> </join> </join> </class> </class> <class name="Address"> <class name="Address">
<id name="id" column="addressId"> <id name="id" column="addressId">
<generator class="native"/> <generator class="native"/> </id> </id> </class> </class>
create table Person ( personId bigint not null primary key ) create table Person ( personId bigint not null primary key )
create table PersonAddress ( personId bigint not null primary key, create table PersonAddress ( personId bigint not null primary key, addressId bigint not null )
addressId bigint not null )
create table Address ( addressId bigint not null primary key ) create table Address ( addressId bigint not null primary key )
7.3.3. one to one
7.3.3. one to one
A
A unidirectional one-to-one association on a join table
unidirectional one-to-one association on a join table is extremely unusual, but
is extremely unusual, but
possible.
possible.
<class name="Person"> <class name="Person">
<id name="id" column="personId"> <id name="id" column="personId"> <generator class="native"/> <generator class="native"/> </id> </id> <join table="PersonAddress" <join table="PersonAddress" optional="true"> optional="true"> <key column="personId" <key column="personId" unique="true"/> unique="true"/> <many-to-one name="address" <many-to-one name="address" column="addressId" column="addressId" not-null="true" not-null="true" unique="true"/> unique="true"/> </join> </join> </class> </class> <class name="Address"> <class name="Address">
<id name="id" column="addressId"> <id name="id" column="addressId">
<generator class="native"/> <generator class="native"/> </id> </id> </class> </class>
create table Person ( personId bigint not null primary key ) create table Person ( personId bigint not null primary key )
create table PersonAddress ( personId bigint not null primary key, create table PersonAddress ( personId bigint not null primary key, addressId bigint not null unique )
addressId bigint not null unique )
create table Address ( addressId bigint not null primary key ) create table Address ( addressId bigint not null primary key )
7.3.4. many to many
7.3.4. many to many
Finally
Finally, we h
, we have a
ave a unidirectional many-to-many association
unidirectional many-to-many association..
<class name="Person"><class name="Person">
<id name="id" column="personId"> <id name="id" column="personId"> <generator class="native"/> <generator class="native"/> </id>
</id>
<set name="addresses" table="PersonAddress"> <set name="addresses" table="PersonAddress">
<key column="personId"/> <key column="personId"/> <many-to-many column="addressId" <many-to-many column="addressId" class="Address"/> class="Address"/> </set> </set> </class> </class> <class name="Address"> <class name="Address">
<id name="id" column="addressId"> <id name="id" column="addressId">
<generator class="native"/> <generator class="native"/> </id> </id> </class> </class>
create table Person ( personId bigint not null primary key ) create table Person ( personId bigint not null primary key )
create table PersonAddress ( personId bigint not null, addressId bigint create table PersonAddress ( personId bigint not null, addressId bigint not null, primary key (personId, addressId) )
not null, primary key (personId, addressId) )
create table Address ( addressId bigint not null primary key ) create table Address ( addressId bigint not null primary key )
7.4. Bidirectional associations
7.4. Bidirectional associations
7.4.1. one to many / many to one
7.4.1. one to many / many to one
A
A bidirectional many-to-one association
bidirectional many-to-one association is the most common kind of association. (This is
is the most common kind of association. (This is
the standard parent/child relationship.)
the standard parent/child relationship.)
<class name="Person"><class name="Person">
<id name="id" column="personId"> <id name="id" column="personId"> <generator class="native"/> <generator class="native"/> </id> </id> <many-to-one name="address" <many-to-one name="address" column="addressId" column="addressId" not-null="true"/> not-null="true"/> </class> </class> <class name="Address"> <class name="Address">
<id name="id" column="addressId"> <id name="id" column="addressId">
<generator class="native"/> <generator class="native"/> </id>
</id>
<set name="people" inverse="true"> <set name="people" inverse="true">
<key column="addressId"/> <key column="addressId"/>
<one-to-many class="Person"/> <one-to-many class="Person"/> </set> </set> </class> </class>
create table Person ( personId bigint not null primary key, addressId create table Person ( personId bigint not null primary key, addressId bigint not null )
bigint not null )
create table Address ( addressId bigint not null primary key ) create table Address ( addressId bigint not null primary key )
If you use a
If you use a
ListList(or other indexed collection) you need to set the
(or other indexed collection) you need to set the
keykeycolumn of the
column of the
foreign key to
foreign key to
not nullnot null, and let Hibernate manage the association from the collections
, and let Hibernate manage the association from the collections
side to maintain the index o
side to maintain the index of each element (making the other
f each element (making the other side virtually inverse by
side virtually inverse by
setting
setting
update="false"update="false"and
and
insert="false"insert="false"):
):
<class name="Person"> <class name="Person"> <id name="id"/> <id name="id"/> ... ... <many-to-one name="address" <many-to-one name="address" column="addressId" column="addressId" not-null="true" not-null="true" insert="false" insert="false" update="false"/> update="false"/> </class> </class> <class name="Address"> <class name="Address"> <id name="id"/> <id name="id"/> ... ... <list name="people"> <list name="people"><key column="addressId" not-null="true"/> <key column="addressId" not-null="true"/> <list-index column="peopleIdx"/> <list-index column="peopleIdx"/> <one-to-many class="Person"/> <one-to-many class="Person"/> </list> </list> </class> </class>
It is important that you define
It is important that you define
not-null="true"not-null="true"on the
on the
<key><key>element of the collection
element of the collection
mapping if the underlying foreign ke
mapping if the underlying foreign key column is
y column is
NOT NULLNOT NULL. Don't only declare
. Don't only declare
not- not-null="true"null="true"
on a possible nested
on a possible nested
<column><column>element, but on the
element, but on the
<key><key>element.
element.
7.4.2. one to one
7.4.2. one to one
A
A bidirectional one-to-one association on a foreign key
bidirectional one-to-one association on a foreign key is quite common.
is quite common.
<class name="Person"><class name="Person">
<id name="id" column="personId"> <id name="id" column="personId"> <generator class="native"/> <generator class="native"/> </id> </id> <many-to-one name="address" <many-to-one name="address" column="addressId" column="addressId" unique="true" unique="true" not-null="true"/> not-null="true"/> </class> </class> <class name="Address"> <class name="Address">
<id name="id" column="addressId"> <id name="id" column="addressId">
<generator class="native"/> <generator class="native"/> </id> </id> <one-to-one name="person" <one-to-one name="person" property-ref="address"/> property-ref="address"/> </class> </class>
create table Person ( personId bigint not null primary key, addressId create table Person ( personId bigint not null primary key, addressId bigint not null unique )
bigint not null unique )
create table Address ( addressId bigint not null primary key ) create table Address ( addressId bigint not null primary key )
A
A bidirectional one-to-one association on a primary key
bidirectional one-to-one association on a primary key uses the special id
uses the special id generator.
generator.
<class name="Person"><class name="Person">
<id name="id" column="personId"> <id name="id" column="personId"> <generator class="native"/> <generator class="native"/> </id> </id> <one-to-one name="address"/> <one-to-one name="address"/> </class> </class> <class name="Address"> <class name="Address">
<id name="id" column="personId"> <id name="id" column="personId"> <generator class="foreign"> <generator class="foreign"> <param name="property">person</param> <param name="property">person</param> </generator> </generator> </id> </id> <one-to-one name="person" <one-to-one name="person" constrained="true"/> constrained="true"/> </class> </class>
create table Person ( personId bigint not null primary key ) create table Person ( personId bigint not null primary key ) create table Address ( personId bigint not null primary key ) create table Address ( personId bigint not null primary key )
7.5. Bidirectional associations with join tables
7.5. Bidirectional associations with join tables
7.5.1. one to many / many to one
7.5.1. one to many / many to one
A
A bidirectional one-to-many association on a join table
bidirectional one-to-many association on a join table. Note that the
. Note that the
inverse="true"inverse="true"can go on either end of the association, on the collection, or on the join.
can go on either end of the association, on the collection, or on the join.
<class name="Person"><class name="Person">
<id name="id" column="personId"> <id name="id" column="personId"> <generator class="native"/> <generator class="native"/> </id> </id> <set name="addresses" <set name="addresses" table="PersonAddress"> table="PersonAddress"> <key column="personId"/> <key column="personId"/> <many-to-many column="addressId" <many-to-many column="addressId" unique="true" unique="true" class="Address"/> class="Address"/> </set> </set> </class> </class> <class name="Address"> <class name="Address">
<id name="id" column="addressId"> <id name="id" column="addressId">
<generator class="native"/> <generator class="native"/> </id> </id> <join table="PersonAddress" <join table="PersonAddress" inverse="true" inverse="true" optional="true"> optional="true"> <key column="addressId"/> <key column="addressId"/> <many-to-one name="person" <many-to-one name="person" column="personId" column="personId" not-null="true"/> not-null="true"/> </join> </join> </class> </class>
create table Person ( personId bigint not null primary key ) create table Person ( personId bigint not null primary key )
create table PersonAddress ( personId bigint not null, addressId bigint create table PersonAddress ( personId bigint not null, addressId bigint not null primary key )
not null primary key )
create table Address ( addressId bigint not null primary key ) create table Address ( addressId bigint not null primary key )
7.5.2. one to one
7.5.2. one to one
A
A bidirectional one-to-one association on a join table
bidirectional one-to-one association on a join table is extremely unusual, but possible.
is extremely unusual, but possible.
<class name="Person"><class name="Person">
<id name="id" column="personId"> <id name="id" column="personId"> <generator class="native"/> <generator class="native"/> </id> </id> <join table="PersonAddress" <join table="PersonAddress" optional="true"> optional="true"> <key column="personId" <key column="personId" unique="true"/> unique="true"/> <many-to-one name="address" <many-to-one name="address" column="addressId" column="addressId" not-null="true" not-null="true" unique="true"/> unique="true"/> </join> </join> </class> </class> <class name="Address"> <class name="Address">
<id name="id" column="addressId"> <id name="id" column="addressId">
<generator class="native"/> <generator class="native"/> </id> </id> <join table="PersonAddress" <join table="PersonAddress" optional="true" optional="true" inverse="true"> inverse="true"> <key column="addressId" <key column="addressId" unique="true"/> unique="true"/> <many-to-one name="person" <many-to-one name="person" column="personId" column="personId" not-null="true" not-null="true" unique="true"/> unique="true"/> </join> </join> </class> </class>
create table Person ( personId bigint not null primary key ) create table Person ( personId bigint not null primary key )
create table PersonAddress ( 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 ) create table Address ( addressId bigint not null primary key )
7.5.3. many to many
7.5.3. many to many
Finally
Finally, we h
, we have a
ave a bidirectional many-to-many association
bidirectional many-to-many association..
<class name="Person"><class name="Person">
<id name="id" column="personId"> <id name="id" column="personId"> <generator class="native"/> <generator class="native"/> </id>
</id>
<set name="addresses" table="PersonAddress"> <set name="addresses" table="PersonAddress">
<key column="personId"/> <key column="personId"/> <many-to-many column="addressId" <many-to-many column="addressId" class="Address"/> class="Address"/> </set> </set> </class> </class> <class name="Address"> <class name="Address">
<id name="id" column="addressId"> <id name="id" column="addressId">
<generator class="native"/> <generator class="native"/> </id>
</id>
<set name="people" inverse="true" table="PersonAddress"> <set name="people" inverse="true" table="PersonAddress">
<key column="addressId"/> <key column="addressId"/> <many-to-many column="personId" <many-to-many column="personId" class="Person"/> class="Person"/> </set> </set> </class> </class>
create table Person ( personId bigint not null primary key ) create table Person ( personId bigint not null primary key )
create table PersonAddress ( personId bigint not null, addressId bigint create table PersonAddress ( personId bigint not null, addressId bigint not null, primary key (personId, addressId) )
not null, primary key (personId, addressId) )
create table Address ( addressId bigint not null primary key ) create table Address ( addressId bigint not null primary key )
7.6. More complex association mappings
7.6. More complex association mappings
More complex association joins are
More complex association joins are extremely
extremely rare. Hibernate makes it possible to handle
rare. Hibernate makes it possible to handle
more complex situations using SQL fragments embedded in the mapping
more complex situations using SQL fragments embedded in the mapping document. For
document. For
example, if a table with h
example, if a table with historical account information data defines
istorical account information data defines
accountNumberaccountNumber,,
effectiveEndDateeffectiveEndDate
and
and
effectiveStartDateeffectiveStartDatecolumns, mapped as follows:
columns, mapped as follows:
<properties name="currentAccountKey"><properties name="currentAccountKey">
<property name="accountNumber" type="string" not-null="true"/> <property name="accountNumber" type="string" not-null="true"/> <property name="currentAccount" type="boolean">
<property name="currentAccount" type="boolean">
<formula>case when effectiveEndDate is null then 1 else 0 <formula>case when effectiveEndDate is null then 1 else 0 end</formula> end</formula> </property> </property> </properties> </properties>
<property name="effectiveEndDate" type="date"/> <property name="effectiveEndDate" type="date"/>
<property name="effectiveStateDate" type="date" not-null="true"/> <property name="effectiveStateDate" type="date" not-null="true"/>
Then we can map an association to the
Then we can map an association to the current
current instance (the one with null
instance (the one with null
effectiveEndDateeffectiveEndDate
) using:
) using:
<many-to-one name="currentAccountInfo" <many-to-one name="currentAccountInfo" property-ref="currentAccountKey" property-ref="currentAccountKey" class="AccountInfo"> class="AccountInfo"> <column name="accountNumber"/> <column name="accountNumber"/> <formula>'1'</formula> <formula>'1'</formula> </many-to-one> </many-to-one>
In a more complex example, imagine that the association between
In a more complex example, imagine that the association between
EmployeeEmployeeand
and
OrganizationOrganization
is maintained in an
is maintained in an
EmploymentEmploymenttable full of historical employment data.
table full of historical employment data.
Then an association to the employee's
Then an association to the employee's most recent
most recent employer (the one with the most recent
employer (the one with the most recent
startDatestartDate
) might be mapped this wa
) might be mapped this way:
y:
<join> <join> <key column="employeeId"/> <key column="employeeId"/> <subselect> <subselect>select employeeId, orgId select employeeId, orgId from Employments
from Employments group by orgId group by orgId
having startDate = max(startDate) having startDate = max(startDate) </subselect> </subselect> <many-to-one name="mostRecentEmployer" <many-to-one name="mostRecentEmployer" class="Organization" class="Organization" column="orgId"/> column="orgId"/> </join> </join>