Self Reference Relationships là gì ?

Seft reference được sử dụng khi chúng ta có mối quan hệ của 2 đối tượng thuộc cùng 1 kiểu entity, hay còn được gọi là quan hệ đệ quy

Ta có quan hệ đệ quy kiểu 1-1, 1-N, N-N

Source code tham khảo: https://github.com/TechMaster/SpringBoot28Days/tree/27a0de8d52f72c30632a1ec9820d1e912305a554/15-CustomRepository/relation

1. Ví dụ

- Ví dụ 1 : Ta có entity Employee cùng đại diện cho 2 đối tượng là Nhân Viên và Quản Lý, nhân viên sẽ có một quản lý trực tiếp quản lý anh ta, quản lý sẽ quản lý nhiều nhân viên.

- Ví dụ 2 : Ta có ví dụ về mối quan hệ giữa các thành viên trong một gia đình, ta có entity Human cùng đại diện cho cha, mẹ,và con. Con sẽ có một cha và một mẹ, cha và mẹ sẽ có nhiều con.

Ta cùng tìm hiểu cách mapping cho 2 ví dụ trên

Human Entity

@Entity(name = "human")
@Table(name = "human")
public class Human {
//...
  @ManyToOne(fetch = FetchType.LAZY)
  @JsonIgnore
  private Human mother;

  @ManyToOne(fetch = FetchType.LAZY)
  @JsonIgnore
  private Human father;

  @ManyToMany
  @JsonIgnore
  private List<Human> parents = new ArrayList<>();

  @ManyToMany(mappedBy = "parents")
  @JsonIgnore
  private List<Human> children = new ArrayList<>();
}
  • Ta thấy human entiy chứa quan hệ đệ quy ManyToOne - 1 cha có nhiều con, ManyToMany - 1 con sẽ có cha và mẹ. Cha và mẹ có thể có nhiều con.
  • Trong quan hệ đệ quy trên sẽ sinh ra thêm 1 bảng mới là human_parents sinh ra từ quan hệ ManyToMany, cột children_id và parents_id sẽ link đến cột id của bảng person.
  • Hai cột khóa ngoại father_id, mother_id sinh ra từ quan hệ ManyToOne, father_id và mother_id sẽ link đến cột id của bảng person

Employee Entity

@Entity(name="employee")
@Table(name="employee")
public class Employee {
  @ManyToOne(fetch = FetchType.LAZY)
  @JsonIgnore
  private Employee manager; //Sếp trực tiếp

  @ManyToMany(mappedBy = "manager")
  @JsonIgnore
  private List<Employee> staffs;
}

Chú thích

  • Nếu bạn không chỉ định FetchType là LAZY thì @ManyToOne sẽ sử dụng fetch type EAGER là mặc định, bạn nên lưu ý performent khi sử dụng fetch type là EAGER .
  • @Annotation transient được sử dụng để chỉ ra trường đó sẽ không được cho vào persistent

Thực thi ứng dụng

  @Transactional
  public void generateEmployee() {
    Employee billGates = new Employee("Bill Gates", null);

    Employee steveBallmer = new Employee("Steve Ballmer", billGates);
    Employee satyaNadella = new Employee("Satya Nadella", billGates);

    Employee john = new Employee("John", satyaNadella);
    Employee bob = new Employee("Bob", john);
    Employee alice = new Employee("Alice", john);
    Employee james = new Employee("James", alice);

    em.persist(billGates); em.persist(steveBallmer); em.persist(satyaNadella);
    em.persist(john); em.persist(bob); em.persist(alice); em.persist(james);
    em.flush();
  }
@Transactional
  public void generateFamilyTree() {
    Human p1 = new Human("Bob", null, null);
    Human p2 = new Human("Alice", null, null);
    Human p3 = new Human("Bob - Alice Joan", p1, p2);
    Human p4 = new Human("Bob - Alice Greenwood", p1, p2);

    Human p5 = new Human("Cate", null, null);
    Human p6 = new Human("Kane", null, null);
    Human p7 = new Human("Joan - Kane James", p3, p6);
    Human p8 = new Human("Cate - Kane Anna", p5, p6);
    Human p9 = new Human("Cate - Kane Roberto", p5, p6);

    em.persist(p1); em.persist(p2); em.persist(p3); em.persist(p4);
    em.persist(p5); em.persist(p6); em.persist(p7); em.persist(p8);
    em.persist(p9);
    em.flush();
  }

Chạy ứng dụng lên ta thu được kết quả có dạng như sau

// http://localhost:8080/family

[
  {
    "id": 1,
    "name": "Bob",
    "children": [
      "Bob - Alice Joan",
      "Bob - Alice Greenwood"
    ]
  },
  {
    "id": 2,
    "name": "Alice",
    "children": [
      "Bob - Alice Joan",
      "Bob - Alice Greenwood"
    ]
  },
  {
    "id": 3,
    "name": "Bob - Alice Joan",
    "parents": [
      "Bob",
      "Alice"
    ],
    "children": [
      "Joan - Kane James"
    ]
  },
  {
    "id": 4,
    "name": "Bob - Alice Greenwood",
    "parents": [
      "Bob",
      "Alice"
    ]
  },
  {
    "id": 5,
    "name": "Cate",
    "children": [
      "Cate - Kane Anna",
      "Cate - Kane Roberto"
    ]
  },
  {
    "id": 6,
    "name": "Kane",
    "children": [
      "Joan - Kane James",
      "Cate - Kane Anna",
      "Cate - Kane Roberto"
    ]
  },
  {
    "id": 7,
    "name": "Joan - Kane James",
    "parents": [
      "Bob - Alice Joan",
      "Kane"
    ]
  },
  {
    "id": 8,
    "name": "Cate - Kane Anna",
    "parents": [
      "Cate",
      "Kane"
    ]
  },
  {
    "id": 9,
    "name": "Cate - Kane Roberto",
    "parents": [
      "Cate",
      "Kane"
    ]
  }
]
/ 20210522180732
// http://localhost:8080/employee

[
  {
    "id": 1,
    "name": "Bill Gates",
    "staffs": [
      "Steve Ballmer",
      "Satya Nadella"
    ]
  },
  {
    "id": 2,
    "name": "Steve Ballmer",
    "manager": "Bill Gates",
    "staffs": [

    ]
  },
  {
    "id": 3,
    "name": "Satya Nadella",
    "manager": "Bill Gates",
    "staffs": [
      "John"
    ]
  },
  {
    "id": 4,
    "name": "John",
    "manager": "Satya Nadella",
    "staffs": [
      "Bob",
      "Alice"
    ]
  },
  {
    "id": 5,
    "name": "Bob",
    "manager": "John",
    "staffs": [

    ]
  },
  {
    "id": 6,
    "name": "Alice",
    "manager": "John",
    "staffs": [
      "James"
    ]
  },
  {
    "id": 7,
    "name": "James",
    "manager": "Alice",
    "staffs": [

    ]
  }
]