一對多是最常見的關聯類型。EJB 3 提供 @OneToMany(多方)與 @ManyToOne(單方)雙向映射,支援外鍵直連與關聯表兩種模式。本文以 Customer(客戶)與 Order(訂單)為例,完整示範兩種映射方式。
id (PK) | name | ...
id (PK) | product_id | count | customer_id (FK → t_customers.id)

@Entity
@Table(name = "t_customers")
public class Customer {
private int id;
private String name;
private Referee referee;
private Collection<Order> orders;
@OneToMany(mappedBy = "customer", cascade = CascadeType.ALL)
public Collection<Order> getOrders() { return orders; }
public void setOrders(Collection<Order> orders) { this.orders = orders; }
// ... 其他 getter/setter
}
@Entity
@Table(name = "t_orders")
public class Order {
private int id;
private String productId;
private int count;
private Customer customer;
@ManyToOne
@JoinColumn(name = "customer_id") // 指定外鍵欄位
public Customer getCustomer() { return customer; }
public void setCustomer(Customer customer) { this.customer = customer; }
// ... 其他 getter/setter
}
mappedBy="customer":指向 Order 端的關聯屬性名,表示由 Order 維護外鍵@JoinColumn(name="customer_id"):指定 t_orders 表的外鍵欄位名cascade=ALL:持久化 Customer 時級聯持久化/更新/刪除所有 OrderCustomer customer = new Customer();
customer.setName("微軟");
List<Order> orders = new ArrayList<Order>();
Order order1 = new Order();
order1.setProductId("1234");
order1.setCount(20);
order1.setCustomer(customer); // 設定反向關聯
orders.add(order1);
Order order2 = new Order();
order2.setProductId("4321");
order2.setCount(12);
order2.setCustomer(customer);
orders.add(order2);
customer.setOrders(orders);
em.persist(customer); -- 級聯持久化 2 筆訂單
t_orders 表移除 customer_id 欄位,新增關聯表:
t_customers_id (FK) | orders_id (FK)

@OneToMany
@JoinTable(
name = "t_customers_orders",
joinColumns = @JoinColumn(name = "t_customers_id", referencedColumnName = "id"),
inverseJoinColumns = @JoinColumn(name = "orders_id", referencedColumnName = "id")
)
public Collection<Order> getOrders() { return orders; }
移除 customer 屬性及其 getter/setter(不再需要反向導航)。
// 必須先持久化 Order,再持久化 Customer
em.persist(order1);
em.persist(order2);
customer.setOrders(orders);
em.persist(customer);
因為關聯表需雙方 ID 才能建立記錄,Order 必須先有 ID。
| 維度 | 外鍵模式 | 關聯表模式 |
|---|---|---|
| 資料表結構 | 多端表有 FK 欄位 | 需額外關聯表 |
| 導航方向 | 雙向 (mappedBy) | 單向 (Customer→Order) |
| 持久化順序 | 任意 (有 cascade) | 必須先存多端 |
| 適用場景 | 標準主從關係 | 多對多退化、歷史系統無 FK、需關聯表掛載屬性 |
| 刪除行為 | Cascade.REMOVE 可級聯刪除 | 需手動清理關聯表或資料庫 CASCADE |
List 而非 Collection:保持順序、支援 @OrderColumn 排序CascadeType.REMOVE 濫用:刪除客戶時自動刪除所有訂單風險極大,建議軟刪除或業務邏輯控制customer.getOrders() 會拋 LazyInitializationException,使用 FETCH JOIN 或 DTO 轉換