Hibernate 中一對一關聯分為兩種實現方式:
<one-to-one constrained="true"> + <generator class="foreign">。<many-to-one unique="true"> 或 <one-to-one property-ref="...">。業務場景:Person (主控) 1:1 Address (被控)。Person 表主鍵 id,Address 表主鍵 id 同時作為外鍵指向 Person。
// 主控方 Person
public class Person {
private Long id;
private String username;
private Address address; // 關聯對象
// Getter/Setter...
}
// 被控方 Address
public class Address {
private Long id; // 共享主鍵
private String addressName;
private Person person; // 反向引用
// Getter/Setter, equals/hashCode...
}
<!-- Person 映射 -->
<class name="Person" table="person">
<id name="id" column="id" type="long">
<generator class="native"/> <!-- 主控方自主生成主鍵 -->
</id>
<property name="username" column="username" type="string"/>
<one-to-one name="address" class="Address" cascade="all" constrained="false"/>
</class>
<!-- Address 映射 -->
<class name="Address" table="address">
<id name="id" column="id" type="long">
<generator class="foreign"> <!-- 關鍵:外鍵生成器 -->
<param name="property">person</param> <!-- 指向關聯屬性 -->
</generator>
</id>
<one-to-one name="person" class="Person" constrained="true"/> <!-- constrained=true 表明此表外鍵約束指向 Person -->
<property name="addressName" column="address_name" type="string"/>
</class>
constrained="false" (Person 端) / "true" (Address 端):只能有一端為 true,表示該端表具有外鍵約束。Address 表主鍵同時是指向 Person 的外鍵,故 Address 端 true。generator class="foreign":保存 Address 時,Hibernate 讀取關聯的 Person.id 作為 Address.id,實現主鍵共享。cascade="all":Person 保存/刪除時級聯操作 Address。person.setAddress(addr); addr.setPerson(person);。業務場景:User 1:1 Profile。User 表有 profile_id 列,UNIQUE 約束指向 Profile 表主鍵。
<class name="User" table="users">
<id name="id" column="id"><generator class="native"/></id>
<property name="username"/>
<!-- 方式 A: many-to-one + unique=true (推薦) -->
<many-to-one name="profile" class="Profile" column="profile_id" unique="true" cascade="all"/>
<!-- 方式 B: one-to-one property-ref (舊版) -->
<!-- <one-to-one name="profile" class="Profile" property-ref="user"/> -->
</class>
<class name="Profile" table="profiles">
<id name="id" column="id"><generator class="native"/></id>
<property name="bio"/>
<!-- 反向引用(可選) -->
<one-to-one name="user" class="User" property-ref="profile"/>
</class>
| 維度 | 主鍵關聯 | 唯一外鍵關聯 |
|---|---|---|
| 表結構 | 雙方共享 PK,被控方 PK 即 FK | 各自獨立 PK,主控方有唯一 FK 列 |
| 插入順序 | 必須先插主控方,再插被控方 | 順序靈活(可先插 Profile 再關聯 User) |
| 級聯刪除 | 自然級聯(FK 約束) | 需配置 cascade 或資料庫 FK ON DELETE |
| 適用場景 | 強生命週期綁定(如:用戶-用戶詳情、訂單-支付詳情) | 弱綁定、可選關聯(如:用戶-擴展配置、員工-工位) |
| 映射複雜度 | 較高 (foreign generator, constrained) | 較低 (many-to-one unique=true) |
// 主鍵關聯
@Entity public class Person {
@Id @GeneratedValue Long id;
@OneToOne(mappedBy = "person", cascade = CascadeType.ALL)
private Address address;
}
@Entity public class Address {
@Id Long id; // 無 @GeneratedValue
@MapsId // 關鍵:主鍵來自關聯端
@OneToOne @JoinColumn(name = "id")
private Person person;
}
// 唯一外鍵關聯
@Entity public class User {
@Id @GeneratedValue Long id;
@OneToOne(cascade = CascadeType.ALL)
@JoinColumn(name = "profile_id", unique = true)
private Profile profile;
}
UNIQUE INDEX,資料庫層面保證 1:1 語義。掌握兩種一對一映射本質,能根據業務強弱綁定特性做出最優表結構與映射決策。