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

Hibernate 多對多關聯映射實戰:人員-地址雙向關聯完整配置與測試

     編輯:Java 綜合教學

場景描述

一個人可住多個地址,一個地址可住多人,典型多對多。中間表 people_address 存儲關聯。本文給出基於 JPA 註解的雙向映射完整配置、表結構生成、CRUD 測試代碼與 SQL 日誌分析。

實體類映射

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;

    @ManyToMany(targetEntity=Address.class)  // 無 mappedBy → 維護端
    @JoinTable(name="people_address",
        joinColumns=@JoinColumn(name="peopleId", referencedColumnName="people_id"),
        inverseJoinColumns=@JoinColumn(name="addressId", referencedColumnName="address_id"))
    private Set<Address> address = new HashSet<>();
    // getter/setter
}

Address 端(被維護端)

@Entity @Table(name="Address_inf")
public class Address {
    @Id @GeneratedValue(strategy=GenerationType.IDENTITY)
    @Column(name="address_id") private int addressId;
    private String message;

    @ManyToMany(targetEntity=People.class)
    @JoinTable(name="people_address",  // 必須與維護端同名
        joinColumns=@JoinColumn(name="addressId", referencedColumnName="address_id"),
        inverseJoinColumns=@JoinColumn(name="peopleId", referencedColumnName="people_id"))
    private Set<People> people = new HashSet<>();
    // getter/setter
}

關鍵配置解讀

註解/屬性作用注意點
@ManyToMany聲明多對多雙向雙方都要寫
targetEntity指定關聯實體集合泛型可推導時可省略
@JoinTable定義中間表雙方必須同名
joinColumns本端外鍵referencedColumnName 指向主鍵列
inverseJoinColumns對端外鍵同上
mappedBy放棄維護權僅一方寫,另一方寫 @JoinTable

測試代碼與 SQL 日誌

// test01.java
Transaction tx = session.beginTransaction();

People p1 = new People(); p1.setAge(22); p1.setName("林彪");
session.save(p1);

Address a1 = new Address(); a1.setMessage("廣州");
a1.getPeople().add(p1);  // 從 Address 端建立關聯
session.persist(a1);

Address a2 = new Address(); a2.setMessage("香港");
a2.getPeople().add(p1);
session.persist(a2);

People p2 = new People(); p2.setAge(17); p2.setName("小芳");
p2.getAddress().add(a2); // 從 People 端建立關聯
session.save(p2);

tx.commit();

Hibernate 執行 SQL 序列

Hibernate: insert into people_inf (age, name) values (?, ?)  -- p1
Hibernate: insert into Address_inf (message) values (?)       -- a1
Hibernate: insert into Address_inf (message) values (?)       -- a2
Hibernate: insert into people_inf (age, name) values (?, ?)   -- p2
Hibernate: insert into people_address (addressId, peopleId) values (?, ?)  -- a1-p1
Hibernate: insert into people_address (addressId, peopleId) values (?, ?)  -- a2-p1
Hibernate: insert into people_address (peopleId, addressId) values (?, ?)  -- p2-a2

觀察:雙向關聯會產生重複中間表插入(a1-p1、p1-a1 邏輯相同但 SQL 列順序不同),Hibernate 4.x 會去重,舊版可能報主鍵衝突。生產建議單向維護或在 Service 層統一入口。

表結構驗證

-- people_inf
people_id (PK) | name | age
-- Address_inf
address_id (PK) | message
-- people_address
peopleId (FK) | addressId (FK)  -- 聯合主鍵

最佳實踐建議

  1. 單向維護:僅在 People 端配置 @JoinTable,Address 端用 @ManyToMany(mappedBy="address"),避免重複 SQL
  2. 使用 List + @OrderColumn:需有序關聯時替換 Set
  3. 級聯策略謹慎:cascade={PERSIST, MERGE} 通常足夠,避免 REMOVE 誤刪關聯數據
  4. 懶加載陷阱:@ManyToMany 默認 LAZY,Session 關閉後訪問拋 LazyInitializationException → DTO 轉換或 Open Session In View
  5. 索引優化:中間表兩個 FK 分別建索引,聯合主鍵已含首列索引

延伸:單向多對多簡化配置

// People 端(維護端)
@ManyToMany
@JoinTable(name="people_address",
    joinColumns=@JoinColumn(name="peopleId"),
    inverseJoinColumns=@JoinColumn(name="addressId"))
private Set<Address> addresses;

// Address 端(僅查詢)
@ManyToMany(mappedBy="addresses")
private Set<People> peoples;

此時只有 People 端操作會同步中間表,Address 端僅可讀取,SQL 更乾淨。

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