Hibernate 中雙向一對一關聯分為主鍵關聯(共享主鍵)與外鍵關聯(獨立主鍵 + 唯一外鍵)兩種映射策略。本文以「人-身份證」經典模型為例,詳細對比兩種策略的映射配置、表結構差異、級聯行為及優缺點。
Person:id、name、idCardIdCard:id、cardNo、personPERSON (personId PK, name)
IDCARD (personId PK & FK → PERSON.personId, cardNo)
主鍵即外鍵,天然保證 1:1,無需額外唯一約束。
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>
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" 實現級聯保存。PERSON (personId PK, name, idCardNo FK UNIQUE NOT NULL → IDCARD.id)
IDCARD (id PK, cardNo)
Person 表持有唯一外鍵指向 IdCard,IdCard 獨立主鍵。
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 端關聯屬性名,完成雙向綁定。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(如用戶-詳情) |
property-ref,否則雙向導航失效。cascade="delete" 需謹慎,主鍵關聯刪除 Person 會級聯刪 IdCard,外鍵關聯默認不級聯(需配置)。fetch="select" 可能產生 N+1,關鍵路徑建議 fetch="join" 或圖形化抓取。主鍵關聯「緊湊高效、強綁定」,外鍵關聯「鬆耦合、獨立演化」。根據業務實體的生命周期綁定強度選擇:生死與共用主鍵,相對獨立用外鍵。映射配置核心在於 foreign 生成器與 many-to-one unique="true" 的正確使用。