Trong cơ sở dữ liệu quan hệ, mối quan hệ One-to-Many xảy ra khi một bản ghi ở bảng cha tương ứng với nhiều bản ghi ở bảng con. Ngược lại, Many-to-One là khi nhiều bản ghi ở bảng con tham chiếu tới một bản ghi duy nhất ở bảng cha. Hai bảng được liên kết với nhau thông qua khóa ngoại.
1. Cấu hình phía One-to-Many
Đoạn mã dưới đây minh họa thực thể Department – đại diện cho bảng cha, sở hữu danh sách nhiều nhân viên:
package com.example.jpa.relations.model;
import javax.persistence.*;
import java.util.List;
@Entity
@Table(name = "departments")
public class Department {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "dept_id")
private Long deptId;
@Column(name = "dept_name")
private String deptName;
@OneToMany(
targetEntity = Employee.class,
mappedBy = "departmentRef",
fetch = FetchType.EAGER,
cascade = CascadeType.ALL
)
private List<Employee> employees;
public Long getDeptId() {
return deptId;
}
public void setDeptId(Long deptId) {
this.deptId = deptId;
}
public String getDeptName() {
return deptName;
}
public void setDeptName(String deptName) {
this.deptName = deptName;
}
public List<Employee> getEmployees() {
return employees;
}
public void setEmployees(List<Employee> employees) {
this.employees = employees;
}
}
2. Cấu hình phía Many-to-One
Thực thể Employee nằm ở phía nhiều, trỏ về một Department duy nhất:
package com.example.jpa.relations.model;
import com.fasterxml.jackson.annotation.JsonIgnore;
import javax.persistence.*;
@Entity
@Table(name = "employees")
public class Employee {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "emp_id")
private Long empId;
@Column(name = "emp_name")
private String empName;
@Column(name = "dept_id")
private Long deptId;
@ManyToOne
@JoinColumn(
name = "dept_id",
insertable = false,
updatable = false,
referencedColumnName = "dept_id"
)
@JsonIgnore
private Department departmentRef;
public Long getEmpId() {
return empId;
}
public void setEmpId(Long empId) {
this.empId = empId;
}
public String getEmpName() {
return empName;
}
public void setEmpName(String empName) {
this.empName = empName;
}
public Long getDeptId() {
return deptId;
}
public void setDeptId(Long deptId) {
this.deptId = deptId;
}
public Department getDepartmentRef() {
return departmentRef;
}
public void setDepartmentRef(Department departmentRef) {
this.departmentRef = departmentRef;
}
}
3. Phân tích thuộc tính @OneToMany
Annotation @OneToMany được đặt trên một trường kiểu List (hoặc Set) để lưu trữ tập hợp các thực thể con. Các tham số chính:
- targetEntity: chỉ định lớp thực thể sẽ được liên kết (tương ứng với bảng con).
- mappedBy: trỏ tới tên thuộc tính ở phía thực thể con giữ mối quan hệ. Tham số này không được dùng cùng với
@JoinTable, và cũng không được áp dụng trên@ManyToOne. - fetch: chiến lược tải dữ liệu, gồm
LAZY(tải khi cần) vàEAGER(tải ngay). Theo đặc tả JPA, mặc định@OneToManylàLAZY. - cascade: xác định các thao tác lan truyền, bao gồm
ALL,PERSIST,MERGE,REMOVE,REFRESH,DETACH.
Theo đặc tả JPA, phía "nhiều" trong quan hệ hai chiều Many-to-One luôn là chủ sở hữu (owning side), do đó mappedBy không thể khai báo trên @ManyToOne.
4. Phân tích thuộc tính @ManyToOne và @JoinColumn
@ManyToOne: đánh dấu mối quan hệ nhiều-một, trỏ thực thể con về thực thể cha.@JoinColumn: xác định cột khóa ngoại dùng để nối hai bảng. Các tham sốinsertable = falsevàupdatable = falsengăn không cho JPA tự động sinh lệnhINSERT/UPDATEtrên cột này, tránh xung đột với cộtdeptIdvốn đã được khai báo riêng. Tham sốreferencedColumnNamechỉ định cột được tham chiếu ở bảng cha.@JsonIgnore: loại trừ thuộc tínhdepartmentRefkhỏi kết quả JSON khi serialize, nhằm tránh vòng lặp tham chiếu giữa hai thực thể.