Person 擁有 Address(嵌入式組件),Address 關聯多個 School(一對多)。Address 不具備獨立標識,隨 Person 生命週期,適合 @Embeddable 組合映射。
@Entity @Table(name="school_inf")
public class School {
@Id @GeneratedValue(strategy=GenerationType.IDENTITY)
@Column(name="school_id") private Integer id;
private String name;
// getter/setter
}
@Entity @Table(name="people_inf")
public class People implements Serializable {
@Id @GeneratedValue(strategy=GenerationType.IDENTITY)
@Column(name="people_id") private Integer id;
private String name; private int age;
@Embedded // 將 Address 內聯到 people_inf 表
private Address address;
// getter/setter
}
@Embeddable
public class Address {
@Column(name="message") // 映射到 people_inf.message
private String message;
@Parent // 反向引用擁有者 People(可選,用於導航)
private People people;
@OneToMany(targetEntity=School.class) // 組件也可擁有關聯!
@JoinColumn(name="address_id", referencedColumnName="people_id") // 外鍵指向 people_inf.people_id
private Set<School> school = new HashSet<>();
// getter/setter
}
-- people_inf
people_id (PK) | name | age | message -- Address 字段內聯
-- school_inf
school_id (PK) | name | address_id (FK → people_inf.people_id)
關鍵點:school_inf.address_id 外鍵關聯 people_inf.people_id,因為 Address 無獨立主鍵,借用宿主主鍵。
Transaction tx = session.beginTransaction();
People p = new People(); p.setAge(22); p.setName("林彪");
session.save(p); // 先存宿主,生成 people_id
Address addr = new Address(); addr.setMessage("廣州");
p.setAddress(addr); // 雙向綁定
School s1 = new School(); s1.setName("哈佛大學");
School s2 = new School(); s2.setName("社會大學");
session.save(s1); session.save(s2); // 獨立實體需先存
addr.getSchool().add(s1); addr.getSchool().add(s2); // 建立關聯
tx.commit();
Hibernate: insert into people_inf (age, message, name) values (?, ?, ?) -- 含 Address.message Hibernate: insert into school_inf (name) values (?) -- s1 Hibernate: insert into school_inf (name) values (?) -- s2 Hibernate: update school_inf set address_id=? where school_id=? -- 關聯 s1,s2 → people_id
觀察:Address 字段內聯到宿主表,School 獨立插入後通過 update 設置外鍵關聯。
| 註解 | 位置 | 語義 |
|---|---|---|
@Embeddable | 組件類 | 聲明可被嵌入,無主鍵 |
@Embedded | 宿主實體字段 | 將組件字段展開映射到宿主表 |
@Parent | 組件字段 | 注入宿主實體引用,便於組件內導航 |
@AttributeOverride | 宿主實體 | 覆蓋組件字段的列名/屬性(同一組件多次嵌入時必需) |
@AssociationOverride | 宿主實體 | 覆蓋組件內關聯映射(如外鍵列名) |
@Column(nullable=false) 或 JPA 2.1 @Embedded(nullable=false)SELECT a FROM Address a 無效),需通過宿主 SELECT p.address.message FROM People p@OneToMany 在 Address 中,外鍵列必須指向宿主 PK,referencedColumnName 必須精確匹配Serializable| 特徵 | @Embeddable (組件) | @Entity (實體) |
|---|---|---|
| 獨立標識 | 無 | 有 (@Id) |
| 生命週期 | 綁定宿主 | 獨立 |
| 表映射 | 內聯宿主表 | 獨立表 |
| 可被多對象引用 | 否 | 是 |
| 獨立查詢 | 不可 | 可 |
| 適用場景 | 值對象:地址、金額、時間區間、姓名組件 | 領域實體:用戶、訂單、商品 |
@Embeddable 讓值對象以零額外表開銷內聯宿主表,同時保留對象導航與關聯能力(如本例 Address→School)。正確使用可顯著簡化模型、提升查詢性能(少一次 JOIN),但需嚴守「無獨立標識、生命週期綁定宿主」原則。