關聯映射是 ORM 中最複雜也最常用的部分,涵蓋一對一、一對多、多對多,每種又分單向與雙向。本文系統性介紹各類映射策略與資料庫對應結構。
Person 可查詢 Address,但反向不可查:
@Entity
public class Person {
@Id @GeneratedValue
private Long id;
@OneToOne
private Address address; // 無 mappedBy
}
@Entity
public class Address {
@Id @GeneratedValue
private Long id;
private String street, city, country;
}
資料庫以外鍵實現:Person 表含 ADDRESS_ID 欄位(預設命名:關聯表名 + _ + ID)。可用 @JoinColumn 自訂外鍵欄位名。
擁有端(Person)與反端(Address),反端必須指定 mappedBy:
@Entity
public class Address {
@OneToOne(mappedBy = "address")
private Person person;
}
使用 List<CellPhone>,預設採用中間表映射:
@Entity
public class Person {
@OneToMany
private List cellPhones;
}
@Entity
public class CellPhone {
@Id @GeneratedValue
private Long id;
private String manufacture, color;
private Long phoneNo;
}
中間表命名:person_cellphone,欄位為雙方表名 + _ + ID。可用 @JoinColumn 改為外鍵方式映射。
反端(CellPhone)使用 @ManyToOne,擁有端指定 mappedBy:
@Entity
public class Person {
@OneToMany(mappedBy = "person")
private List cellPhones;
}
@Entity
public class CellPhone {
@ManyToOne
private Person person;
}
資料庫採用外鍵方式:CellPhone 表含 PERSON_ID 欄位。
只能透過中間表映射:
@Entity
public class Teacher {
@ManyToMany
private List students;
}
@Entity
public class Student {
@Id @GeneratedValue
private Long id;
private String name, gender, age, height;
}
生成三張表:teacher、student、中間表 teacher_student。
擁有端(Teacher)與反端(Student),反端指定 mappedBy:
@Entity
public class Teacher {
@ManyToMany
private List students;
}
@Entity
public class Student {
@ManyToMany(mappedBy = "students")
private List teachers;
}
資料庫結構與單向相同,差別在物件模型可雙向導航。
| 關係類型 | 擁有端註解 | 反端註解 | 資料庫實現 |
|---|---|---|---|
| 單向 1:1 | @OneToOne | 無 | 擁有端表加外鍵 |
| 雙向 1:1 | @OneToOne | @OneToOne(mappedBy) | 擁有端表加外鍵 |
| 單向 1:N | @OneToMany | 無 | 中間表(預設)或外鍵 |
| 雙向 1:N | @OneToMany(mappedBy) | @ManyToOne | 反端表加外鍵 |
| 單向 M:N | @ManyToMany | 無 | 中間表 |
| 雙向 M:N | @ManyToMany | @ManyToMany(mappedBy) | 中間表 |
關聯映射雖複雜但核心邏輯清晰:擁有端決定資料庫結構,反端透過 mappedBy 聲明關聯。單向僅擁有端導航,雙向雙方可導航。掌握 @JoinColumn、mappedBy、中間表與外鍵兩種實現方式,即可應對大多數業務場景。關聯映射是關係型資料庫特性在物件模型中的體現,值得深入鑽研。