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

EJB 3 開發指南(9):多對多關聯映射完整實作

     編輯:Java 相關知識

多對多關聯需透過關聯表實現。EJB 3 使用 @ManyToMany 搭配 @JoinTable 完成映射。本文以 Customer(客戶)與 Address(地址)為例,示範雙向多對多映射、關聯表配置與測試程式碼。

一、資料表結構

t_customers

id (PK) | name | ...

t_addresses

id (PK) | address_line | country | post_code

t_addresses 表結構

關聯表:t_customers_addresses

customer_id (FK) | address_id (FK)

t_customers_addresses 關聯表結構

複合主鍵:(customer_id, address_id)

二、實體類別映射

Customer 類別(關聯維護端)

@Entity
@Table(name = "t_customers")
public class Customer {
    private int id;
    private String name;
    private Referee referee;
    private Collection<Order> orders;
    private Collection<Address> addresses;

    @ManyToMany(cascade = CascadeType.PERSIST, fetch = FetchType.LAZY)
    @JoinTable(
        name = "t_customers_addresses",
        joinColumns = @JoinColumn(name = "customer_id", referencedColumnName = "id"),
        inverseJoinColumns = @JoinColumn(name = "address_id", referencedColumnName = "id")
    )
    public Collection<Address> getAddresses() { return addresses; }
    public void setAddresses(Collection<Address> addresses) { this.addresses = addresses; }
    // ... 其他 getter/setter 略
}

Address 類別(被關聯端)

@Entity
@Table(name = "t_addresses")
public class Address {
    private int id;
    private String addressLine;
    private String country;
    private String postCode;
    private Collection<Customer> customers;

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    public int getId() { return id; }
    // ... 基本屬性 getter/setter 略

    @ManyToMany(mappedBy = "addresses")
    public Collection<Customer> getCustomers() { return customers; }
    public void setCustomers(Collection<Customer> customers) { this.customers = customers; }
}

三、關鍵註解解析

註解位置說明
@ManyToMany雙方 getter聲明多對多關聯
@JoinTable關聯維護端指定關聯表名稱、雙向外鍵欄位對應
joinColumns@JoinTable 內本方實體對應的外鍵(此處 customer_id → Customer.id)
inverseJoinColumns@JoinTable 內對方實體對應的外鍵(此處 address_id → Address.id)
mappedBy被關聯端指向關聯維護端的屬性名("addresses"),表示不維護關聯表
cascade=PERSIST維護端持久化 Customer 時級聯持久化關聯的 Address
fetch=LAZY維護端預設延遲載入,避免載入大量地址

四、測試程式碼:建立客戶與多地址關聯

Customer customer = new Customer();
customer.setName("微軟11");

List<Address> addresses = new ArrayList<Address>();

Address address1 = new Address();
address1.setAddressLine("address1");
address1.setCountry("中國");
address1.setPostCode("12345678");
addresses.add(address1);

Address address2 = new Address();
address2.setAddressLine("address2");
address2.setCountry("美國");
address2.setPostCode("4321");
addresses.add(address2);

customer.setAddresses(addresses);
em.persist(customer);  // 級聯持久化 2 個 Address 並建立關聯表記錄

五、執行 SQL 行為分析

INSERT INTO t_customers (name) VALUES ('微軟11');           -- 客戶
INSERT INTO t_addresses (address_line, country, post_code) VALUES ('address1', '中國', '12345678');
INSERT INTO t_addresses (address_line, country, post_code) VALUES ('address2', '美國', '4321');
INSERT INTO t_customers_addresses (customer_id, address_id) VALUES (1, 1);
INSERT INTO t_customers_addresses (customer_id, address_id) VALUES (1, 2);

六、常見陷阱與最佳實踐

1. 雙向關聯同步維護

新增關聯時,建議雙向同步:

customer.getAddresses().add(address);
address.getCustomers().add(customer);  // 保持記憶體中物件圖一致

2. 避免無限遞迴序列化

JSON/XML 序列化時,雙向關聯會導致循環引用。解決方案:

  • 使用 @JsonIgnore (Jackson) 或 @XmlTransient (JAXB) 標記一方
  • 或使用 DTO 轉換層切斷關聯

3. 關聯表額外欄位擴充

若關聯表需額外欄位(如 created_date、is_primary),無法直接用 @ManyToMany,需拆解為兩個 @OneToMany + 中間實體類別。

4. 批次操作效能

大量建立關聯時,考慮:

  • 設定 hibernate.jdbc.batch_size
  • 分批 persist 並定期 flush/clear
  • 或使用原生 SQL 批次插入關聯表
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved