Hibernate.orgCommunity Documentation

第 7 章 关联关系映射

7.1. 介绍
7.2. 单向关联(Unidirectional associations)
7.2.1. 多对一(many-to-one)
7.2.2. 一对一(One-to-one)
7.2.3. 一对多(one-to-many)
7.3. 使用连接表的单向关联(Unidirectional associations with join tables)
7.3.1. 一对多(one-to-many)
7.3.2. 多对一(many-to-one)
7.3.3. 一对一(One-to-one)
7.3.4. 多对多(many-to-many)
7.4. 双向关联(Bidirectional associations)
7.4.1. 一对多(one to many)/多对一(many to one)
7.4.2. 一对一(One-to-one)
7.5. 使用连接表的双向关联(Bidirectional associations with join tables)
7.5.1. 一对多(one to many)/多对一(many to one)
7.5.2. 一对一(one to one)
7.5.3. 多对多(many-to-many)
7.6. 更复杂的关联映射

关联关系映射通常情况是最难配置正确的。在这个部分中,我们从单向关系映射开始,然后考虑双向关系映射,逐步讲解典型的案例。在所有的例子中,我们都使将用 PersonAddress

我们根据映射关系是否涉及连接表以及多样性(multiplicity)来划分关联类型。

在传统的数据建模中,允许为 Null 值的外键被认为是一种不好的实践,因此我们所有的例子中都使用不允许为 Null 的外键。这并不是 Hibernate的 要求,即使你删除掉不允许为 Null 的约束,Hibernate 映射一样可以工作的很好。

双向多对一关联 是最常见的关联关系。下面的例子解释了这种标准的父/子关联关系。


<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 )
        

如果你使用 List(或者其他有序集合类),你需要设置外键对应的 key 列为 not null。Hibernate 将从集合端管理关联,维护每个元素的索引,并通过设置 update="false"insert="false" 来对另一端反向操作。


<class name="Person">
   <id name="id"/>
   ...
   <many-to-one name="address"
      column="addressId"
      not-null="true"
      insert="false"
      update="false"/>
</class>

<class name="Address">
   <id name="id"/>
   ...
   <list name="people">
      <key column="addressId" not-null="true"/>
      <list-index column="peopleIdx"/>
      <one-to-many class="Person"/>
   </list>
</class
>

假若集合映射的 <key> 元素对应的底层外键字段是 NOT NULL 的,那么为这一 key 元素定义 not-null="true" 是很重要的。不要仅仅为可能的嵌套 <column>元素定义 not-null="true"<key> 元素也是需要的。

更复杂的关联连接极为罕见。通过在映射文档中嵌入 SQL 片断,Hibernate 也可以处理更为复杂的情况。比如,假若包含历史帐户数据的表定义了 accountNumbereffectiveEndDateeffectiveStartDate 字段,按照下面映射:


<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"/>

那么我们可以对目前(current)实例(其 effectiveEndDate 为 null)使用这样的关联映射:


<many-to-one name="currentAccountInfo"
        property-ref="currentAccountKey"
        class="AccountInfo">
    <column name="accountNumber"/>
    <formula
>'1'</formula>
</many-to-one
>

在更复杂的例子中,假想 EmployeeOrganization 之间的关联是通过一个 Employment 中间表维护的,而中间表中填充了很多历史雇员数据。那“雇员的最新雇主”这个关联(最新雇主就是具有最新的 startDate 的那个)可以这样映射:


<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
>

使用这一功能时可以充满创意和灵活性,但通常更加实用的是用 HQL 或条件查询来处理这些情况。