首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 相關知識

Hibernate 雙向一對一映射全解析:主鍵關聯 vs 外鍵關聯

     編輯:Java 相關知識

前言

Hibernate 中雙向一對一關聯分為主鍵關聯(共享主鍵)與外鍵關聯(獨立主鍵 + 唯一外鍵)兩種映射策略。本文以「人-身份證」經典模型為例,詳細對比兩種策略的映射配置、表結構差異、級聯行為及優缺點。

一、領域模型

  • Person:id、name、idCard
  • IdCard:id、cardNo、person
  • 業務規則:一人一證,一證一人,雙向導航。

二、策略一:雙向主鍵關聯

2.1 表結構

PERSON (personId PK, name)
IDCARD (personId PK & FK → PERSON.personId, cardNo)

主鍵即外鍵,天然保證 1:1,無需額外唯一約束。

2.2 映射配置

IdCard.hbm.xml(關鍵端)

<class name="IdCard" table="IDCARD">
    <id name="id" column="personId">
        <generator class="foreign">
            <param name="property">person</param>
        </generator>
    </id>
    <property name="cardNo" column="cardno"/>
    <one-to-one name="person" constrained="true"/>
</class>
  • foreign 生成器:IdCard 的 id 由關聯的 Person 對象賦值。
  • constrained="true":在 IDCARD 表加外鍵約束,確保 Person 存在。

Person.hbm.xml

<class name="Person" table="PERSON">
    <id name="id" column="personId"><generator class="native"/></id>
    <property name="name" column="personName"/>
    <one-to-one name="idCard"/>
</class>

2.3 保存邏輯與級聯

Person p = new Person(); p.setName("Zhang");
IdCard c = new IdCard(); c.setCardNo("111"); c.setPerson(p);
session.save(p);  // 先存 Person,生成 ID
session.save(c);  // 再存 IdCard,foreign 生成器讀取 p.id
  • 必須先保存主表,再保存從表。
  • 建議在 <one-to-one> 加 cascade="save-update" 實現級聯保存。

三、策略二:雙向外鍵關聯

3.1 表結構

PERSON (personId PK, name, idCardNo FK UNIQUE NOT NULL → IDCARD.id)
IDCARD (id PK, cardNo)

Person 表持有唯一外鍵指向 IdCard,IdCard 獨立主鍵。

3.2 映射配置

Person.hbm.xml(主控端)

<class name="Person" table="PERSON">
    <id name="id" column="personId"><generator class="native"/></id>
    <property name="name" column="NAME"/>
    <many-to-one name="idCard" column="idCardNo" unique="true" not-null="true"/>
</class>
  • 用 <many-to-one unique="true"> 模擬一對一,unique 加唯一約束,not-null 強制關聯。

IdCard.hbm.xml

<class name="IdCard" table="IDCARD">
    <id name="id"><generator class="native"/></id>
    <property name="cardNo" column="CARDNO"/>
    <one-to-one name="person" property-ref="idCard"/>
</class>
  • property-ref="idCard":指定 Person 端關聯屬性名,完成雙向綁定。

3.3 保存邏輯

IdCard c = new IdCard(); c.setCardNo("111");
Person p = new Person(); p.setName("Zhang"); p.setIdCard(c);
session.save(c);  // 先存 IdCard 生成 ID
session.save(p);  // 再存 Person,外鍵寫入 c.id
  • 順序相反:先存被引用端,再存持外鍵端。

四、深度對比

維度主鍵關聯外鍵關聯
表結構共享主鍵,PK=FK獨立主鍵 + 唯一 FK
映射標簽雙端 <one-to-one> + foreign 生成器主控端 <many-to-one unique="true">,被控端 <one-to-one property-ref>
保存順序先主表後從表先被引用表後持 FK 表
查詢性能主鍵關聯查詢極快需 JOIN 或二級緩存
業務靈活性IdCard 不能獨立存在,強綁定IdCard 可獨立存在,解耦度高
適用場景實體間強生命周期綁定(如訂單-付款)實體相對獨立但業務 1:1(如用戶-詳情)

五、避坑指南

  1. foreign 生成器要求:關聯對象必須先持久化(有 ID),否則異常。
  2. constrained="true":生成外鍵約束,防止孤兒數據,但會增加 INSERT 開銷。
  3. property-ref 必填:外鍵關聯雙向時,被控端必須指定 property-ref,否則雙向導航失效。
  4. 級聯刪除:cascade="delete" 需謹慎,主鍵關聯刪除 Person 會級聯刪 IdCard,外鍵關聯默認不級聯(需配置)。
  5. 懶加載陷阱:一對一默認 fetch="select" 可能產生 N+1,關鍵路徑建議 fetch="join" 或圖形化抓取。

六、總結

主鍵關聯「緊湊高效、強綁定」,外鍵關聯「鬆耦合、獨立演化」。根據業務實體的生命周期綁定強度選擇:生死與共用主鍵,相對獨立用外鍵。映射配置核心在於 foreign 生成器與 many-to-one unique="true" 的正確使用。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved