Association mappings are often the most difficult thing to implement correctly. In this section we examine some canonical cases one by one, starting with unidirectional mappings and then bidirectional cases. We will use Person and Address in all the examples.
Associations will be classified by multiplicity and whether or not they map to an intervening join table.
Nullable foreign keys are not considered to be good practice in traditional data modelling, so our examples do not use nullable foreign keys. This is not a requirement of NHibernate, and the mappings will work if you drop the nullability constraints.
An unidirectional many-to-one association is the most common kind of unidirectional association.
<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>
create table Person ( personId bigint not null primary key, addressId bigint not null ) create table Address ( addressId bigint not null primary key )
An unidirectional one-to-one association on a foreign key is almost identical to a many-to-one. The only difference is the column unique constraint.
<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>
create table Person ( personId bigint not null primary key, addressId bigint not null unique ) create table Address ( addressId bigint not null primary key )
An unidirectional one-to-one association on a primary key usually uses a special id generator In this example, however, we have reversed the direction of the association:
<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>
create table Person ( personId bigint not null primary key ) create table Address ( personId bigint not null primary key )
An unidirectional one-to-many association on a foreign key is an unusual case, and is not recommended.
<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>
create table Person ( personId bigint not null primary key ) create table Address ( addressId bigint not null primary key, personId bigint not null )
An unidirectional one-to-many association on a join table is the preferred option. Specifying unique="true" changes the multiplicity from many-to-many to one-to-many.
<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>
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 )
An unidirectional many-to-one association on a join table is common when the association is optional (while avoiding nullable foreign key).
<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>
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 )
An unidirectional one-to-one association on a join table is possible, but extremely unusual.
<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>
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 )
Finally, here is an example of an unidirectional many-to-many association.
<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>
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 )
A bidirectional many-to-one association is the most common kind of association. The following example illustrates the standard parent/child relationship.
<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>
create table Person ( personId bigint not null primary key, addressId bigint not null ) create table Address ( addressId bigint not null primary key )
If you use an IList, or other indexed collection, set the key column of the foreign key to not null. NHibernate will manage the association from the collections side to maintain the index of each element, making the other side virtually inverse by setting update="false" and insert="false":
<class name="Person"> <id name="Id" column="personId"> <generator class="native"/> </id> <many-to-one name="Address" column="addressId" not-null="true" insert="false" update="false"/> </class> <class name="Address"> <id name="Id" column="addressId"> <generator class="native"/> </id> <list name="People"> <key column="addressId" not-null="true"/> <list-index column="peopleIdx"/> <one-to-many class="Person"/> </list> </class>
create table Person ( personId bigint not null primary key, addressId bigint not null, peopleIdx int not null ) create table Address ( addressId bigint not null primary key )
When the underlying foreign key column is NOT NULL, it is important that you define not-null="true" on the <key> element of the collection mapping. Do not only declare not-null="true" on a possible nested <column> element, but always declare it on the <key> element too.
A bidirectional one-to-one association on a foreign key is common.
<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>
create table Person ( personId bigint not null primary key, addressId bigint not null unique ) create table Address ( addressId bigint not null primary key )
A bidirectional one-to-one association on a primary key uses the special foreign id generator.
<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>
create table Person ( personId bigint not null primary key ) create table Address ( personId bigint not null primary key )
The following is an example of a bidirectional one-to-many association on a join table. The inverse="true" can go on either end of the association, on the collection, or on the join.
<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>
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 )
A bidirectional one-to-one association on a join table is possible, but extremely unusual.
<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="Person" column="personId" not-null="true" unique="true"/> </join> </class>
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 )
Here is an example of a bidirectional many-to-many association.
<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> <set name="People" inverse="true" table="PersonAddress"> <key column="addressId"/> <many-to-many column="personId" class="Person"/> </set> </class>
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 )
More complex association joins are extremely rare. NHibernate handles more complex situations by using SQL fragments embedded in the mapping document. For example, if a table with historical account information data defines AccountNumber, EffectiveEndDate and EffectiveStartDatecolumns, it would be mapped as follows:
<properties name="CurrentAccountKey"> <property name="AccountNumber" type="string" not-null="true"/> <property name="CurrentAccount" type="boolean"> <formula>case when EffectiveEndDate is null then 1 else 0 end</formula> </property> </properties> <property name="EffectiveEndDate" type="date"/> <property name="EffectiveStateDate" type="date" not-null="true"/>
You can then map an association to the current instance, the one with null EffectiveEndDate, by using:
<many-to-one name="CurrentAccountInfo" property-ref="CurrentAccountKey" class="AccountInfo"> <column name="AccountNumber"/> <formula>'1'</formula> </many-to-one>
In a more complex example, imagine that the association between Employee and Organization is maintained in an Employment table full of historical employment data. An association to the employee's most recent employer, the one with the most recent startDate, could be mapped in the following way:
<join> <key column="employeeId"/> <subselect> select employeeId, orgId from Employments group by orgId having startDate = max(startDate) </subselect> <many-to-one name="MostRecentEmployer" class="Organization" column="orgId"/> </join>
This functionality allows a degree of creativity and flexibility, but it is more practical to handle these kinds of cases by using queries.