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

Hibernate 組合映射實戰:@Embeddable 實現 人-地址-學校 多層級對象圖持久化

     編輯:Java 綜合教學

場景與建模

Person 擁有 Address(嵌入式組件),Address 關聯多個 School(一對多)。Address 不具備獨立標識,隨 Person 生命週期,適合 @Embeddable 組合映射。

實體與組件定義

School(獨立實體)

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

People(根實體)

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

Address(嵌入式組件)

@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();

生成 SQL 分析

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宿主實體覆蓋組件內關聯映射(如外鍵列名)

避坑指南

  1. 組件不可共享:同一 Address 實例不能同時嵌入兩個 People,否則外鍵衝突
  2. 空值處理:宿主字段為 null 時,組件所有列寫 NULL;可用 @Column(nullable=false) 或 JPA 2.1 @Embedded(nullable=false)
  3. 查詢限制:HQL/JPQL 不能直接查詢組件(SELECT a FROM Address a 無效),需通過宿主 SELECT p.address.message FROM People p
  4. 集合關聯在組件中:如本例 @OneToMany 在 Address 中,外鍵列必須指向宿主 PK,referencedColumnName 必須精確匹配
  5. 序列化:組件類建議實現 Serializable

何時選擇 @Embeddable vs @Entity?

特徵@Embeddable (組件)@Entity (實體)
獨立標識無有 (@Id)
生命週期綁定宿主獨立
表映射內聯宿主表獨立表
可被多對象引用否是
獨立查詢不可可
適用場景值對象:地址、金額、時間區間、姓名組件領域實體:用戶、訂單、商品

總結

@Embeddable 讓值對象以零額外表開銷內聯宿主表,同時保留對象導航與關聯能力(如本例 Address→School)。正確使用可顯著簡化模型、提升查詢性能(少一次 JOIN),但需嚴守「無獨立標識、生命週期綁定宿主」原則。

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