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

Hibernate @OneToMany vs @ManyToOne 映射詳解:外鍵在哪邊?

     編輯:MySQL 綜合教學

核心結論:外鍵屬於「多」的一方

@ManyToOne 定義在「多」的一方實體類中,**對應表中生成外鍵列**。
@OneToMany 定義在「一」的一方實體類中,**不生成外鍵列**,僅建立對象圖導航關係。

場景:項目經理 (Manager) 1:N 程序員

正確映射 (單向 @ManyToOne)

// Programmer.java (多的一方)
@Entity
@Table(name = "t_programmer")
public class Programmer {
    @Id @GeneratedValue
    private Long id;
    private String name;

    @ManyToOne(fetch = FetchType.LAZY)  // 默認 EAGER,建議 LAZY
    @JoinColumn(name = "manager_id")    // 指定外鍵列名,默認 manager_id
    private Manager manager;
    // getter/setter
}

// Manager.java (一的一方)
@Entity
@Table(name = "t_manager")
public class Manager {
    @Id @GeneratedValue
    private Long id;
    private String name;
    // 無 @OneToMany,單向關聯
}

生成 DDL:t_programmer 表包含 manager_id BIGINT 外鍵引用 t_manager.id。

雙向映射 (一的一方加 @OneToMany)

// Manager.java
@OneToMany(mappedBy = "manager", cascade = CascadeType.ALL, orphanRemoval = true)
private Set programmers = new HashSet<>();

// Programmer.java 保持不變 (已有 @ManyToOne @JoinColumn)
// 注意:mappedBy = "manager" 指向 Programmer 中的屬性名,告訴 Hibernate「關係由那邊維護」
  • mappedBy 關鍵:聲明關係由「多」的一方 (Programmer.manager) 維护,**不再生成中間表或額外外鍵**。
  • 集合類型:用 Set 避免重複,**必須正確實現 equals/hashCode (基於業務鍵或 ID+版本號)**,否則實體脫管/合併時出現異常。
  • cascade/orphanRemoval:級聯持久化/刪除孤兒,慎用於大集合 (性能/事務大小)。

錯誤示範:雙向雙邊維護 (無 mappedBy)

// Manager.java
@OneToMany
@JoinColumn(name = "manager_id")  // 錯誤!這會在 t_programmer 再建一個外鍵或報錯
private Set programmers;

// Programmer.java
@ManyToOne
@JoinColumn(name = "manager_id")  // 也維護同一列
private Manager manager;

後果:Hibernate 嘗試雙向更新同一外鍵列,產生多餘 UPDATE、死鎖或異常 Multiple representations of same entity。

@OneToMany 單向 (不推薦)

// Manager.java
@OneToMany
@JoinColumn(name = "manager_id")  // 在「一」的一方維護外鍵
private List programmers;

// Programmer.java 無任何關聯註解

缺點:Hibernate 需額外 UPDATE 設置外鍵 (先 INSERT Programmer, 再 UPDATE manager_id);無法在 Programmer 端導航到 Manager;List 需 @OrderColumn 維護順序。**不如雙向或單向 @ManyToOne 自然。**

避坑清單

  1. 默認 FetchType:@ManyToOne 默認 EAGER,**必須顯式 fetch = FetchType.LAZY**,否則查一個 Programmer 連帶加載 Manager 甚至遞歸加載整個對象圖,導致 N+1 或 StackOverflow。
  2. equals/hashCode:實體放入 Set/作為 Map Key 時,**切勿用自增 ID** (瞬時態 ID 為 null),用業務唯一鍵 (如 email) 或「ID + 版本號」或僅比較實例引用 (僅用於新建實體)。
  3. 序列化:雙向關聯易導致 JSON 序列化循環引用,配合 @JsonIgnore / @JsonManagedReference / @JsonBackReference (Jackson) 或 DTO 轉換層隔離。
  4. Schema 生成:hibernate.hbm2ddl.auto=update 僅供開發,**生產必須用 Flyway/Liquibase 管理遷移腳本**。

總結

  • **外鍵物理列永遠在「多」的一方表中**。
  • **@ManyToOne (@JoinColumn) 是關係擁有者**,負責外鍵 DDL/DML。
  • **@OneToMany(mappedBy="...") 僅提供對象導航**,不影響 Schema。
  • **單向 @ManyToOne 最簡潔**;需反向導航時加 @OneToMany(mappedBy=...) 形成雙向。
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved