@ManyToMany

Disponibil

Masa de serviciu

Acum să ne uităm la un alt caz obișnuit - de la mulți la mulți. Să ne imaginăm că avem o relație de la mulți la mulți între sarcini și angajați :

  • Un angajat din tabelul de angajați poate face multe sarcini din tabelul de sarcini.
  • O sarcină din tabelul de sarcini poate fi atribuită mai multor angajați.

Această relație între entități se numește multi-la-mulți. Și pentru a-l implementa la nivel SQL, avem nevoie de un tabel de servicii suplimentar. Să-i numim, de exemplu, angajat_sarcina.

Tabelul employee_task va conține doar două coloane:

  • card de identitate al angajatului
  • task_id

De fiecare dată când atribuim o anumită sarcină unui anumit utilizator, un nou rând va fi adăugat la acest tabel. Exemplu:

card de identitate al angajatului task_id
1 1
1 2
2 3

Ei bine, tabelul de activități ar trebui să-și piardă coloana employee_id . Are sens doar dacă sarcina poate fi atribuită unui singur angajat. Dacă sarcina poate fi atribuită mai multor angajați, atunci aceste informații trebuie să fie stocate în tabelul de serviciu employee_task .

Relația la nivel de masă

Iată cum vor arăta noile noastre tabele:

id Nume ocupaţie salariu vârstă Data înscrierii
1 Ivanov Ivan Programator 100000 25 30-06-2012
2 Petrov Petr Programator 80000 23 12-08-2013
3 Ivanov Serghei Tester 40000 treizeci 01-01-2014
4 Rabinovici Moisha Director 200000 35 2015-05-12
5 Kirienko Anastasia Manager de birou 40000 25 2015-10-10
6 Vaska Pisică 1000 3 2018-11-11

Tabelul angajaților ( neschimbat ) :

Acest tabel are următoarele coloane:

  • id INT
  • nume VARCHAR
  • ocupatie VARCHAR
  • salariu INT
  • vârsta INT
  • join_date DATE

Și așa arată tabelul de activități , a pierdut coloana employee_id (marcată cu roșu):

id Emploee_id Nume Termen limită
1 1 Remediați o eroare pe front-end 2022-06-01
2 2 Remediați o eroare pe backend 2022-06-15
3 5 Cumpără cafea 2022-07-01
4 5 Cumpără cafea 2022-08-01
5 5 Cumpără cafea 2022-09-01
6 (NUL) Curățați biroul (NUL)
7 4 Bucură-te de viață (NUL)
8 6 Bucură-te de viață (NUL)

Acest tabel are acum doar 3 coloane:

  • id - numărul unic al sarcinii (și rândurile din tabel)
  • employee_id - (eliminat)
  • nume - numele și descrierea sarcinii
  • termen - timpul până la care sarcina trebuie finalizată

Avem, de asemenea, tabelul de serviciu employee_task , unde datele employee_id au migrat din tabelul de activități:

card de identitate al angajatului task_id
1 1
2 2
5 3
5 4
5 5
(NUL) 6
4 7
6 8

Am salvat temporar coloana ștearsă în tabelul de activități, astfel încât să puteți vedea că datele din aceasta au fost mutate în tabelul employee_task.

Un alt punct important este linia roșie „(NULL) 6” din tabelul employee_task. L-am marcat cu roșu pentru că nu va fi în tabelul employee_task .

Dacă sarcina 7 este atribuită utilizatorului 4, atunci ar trebui să existe un rând (4, 7) în tabelul employee_task.

Dacă sarcina 6 nu este atribuită nimănui, atunci pur și simplu nu va exista nicio înregistrare pentru ea în tabelul employee_task. Iată cum vor arăta versiunile finale ale acestor tabele:

tabel de sarcini :

id Nume Termen limită
1 Remediați o eroare pe front-end 2022-06-01
2 Remediați o eroare pe backend 2022-06-15
3 Cumpără cafea 2022-07-01
4 Cumpără cafea 2022-08-01
5 Cumpără cafea 2022-09-01
6 Curățați biroul (NUL)
7 Bucură-te de viață (NUL)
8 Bucură-te de viață (NUL)

tabelul task_empleat:

card de identitate al angajatului task_id
1 1
2 2
5 3
5 4
5 5
4 7
6 8

Comunicare la nivel de clasă Java

Dar cu comunicarea la nivel de Entitate-clase, avem o comandă completă. Să începem cu veștile bune.

În primul rând, Hibernate are o adnotare specială @ManyToMany care vă permite să descrieți bine cazul unei relații de tabel multi-la-mulți.

În al doilea rând, două clase de Entități sunt încă suficiente pentru noi. Nu avem nevoie de o clasă pentru masa de serviciu.

Iată cum vor arăta cursurile noastre. Clasa de angajați în forma sa originală:

@Entity
@Table(name="user")
class Employee {
   @Column(name="id")
   public Integer id;

   @Column(name="name")
   public String name;

   @Column(name="occupation")
   public String occupation;

   @Column(name="salary")
   public Integer salary;

   @Column(name="join_date")
   public Date join;
}

Și clasa EmployeeTask în forma sa originală:

@Entity
@Table(name="task")
class EmployeeTask {
   @Column(name="id")
   public Integer id;

   @Column(name="name")
   public String description;

   @Column(name="deadline")
   public Date deadline;
}

Adnotare @ManyToMany

Voi omite câmpurile existente în exemple, dar voi adăuga altele noi. Iată cum vor arăta. Clasa de angajati :

@Entity
@Table(name="employee")
class Employee {
   @Column(name="id")
   public Integer id;

   @ManyToMany(cascade = CascadeType.ALL)
   @JoinTable(name="employee_task",
	       joinColumns=  @JoinColumn(name="employee_id", referencedColumnName="id"),
           inverseJoinColumns= @JoinColumn(name="task_id", referencedColumnName="id") )
   private Set<EmployeeTask> tasks = new HashSet<EmployeeTask>();

}

Și clasa EmployeeTask :

@Entity
@Table(name="task")
class EmployeeTask {
   @Column(name="id")
   public Integer id;

   @ManyToMany(cascade = CascadeType.ALL)
   @JoinTable(name="employee_task",
       	joinColumns=  @JoinColumn(name="task_id", referencedColumnName="id"),
       	inverseJoinColumns= @JoinColumn(name=" employee_id", referencedColumnName="id") )
   private Set<Employee> employees = new HashSet<Employee>();

}

Se pare că totul este complicat, dar de fapt totul este simplu.

În primul rând, folosește adnotarea @JoinTable (a nu se confunda cu @JoinColumn), care descrie tabelul de serviciu employee_task.

În al doilea rând, descrie faptul că coloana task_id a tabelului employee_task se referă la coloana id a tabelului task.

În al treilea rând, spune că coloana employee_id din tabelul employee_task se referă la coloana id din tabelul employee.

De fapt, cu ajutorul adnotărilor, am descris ce date sunt conținute în tabelul employee_task și cum ar trebui să le interpreteze Hibernate.

Dar acum putem adăuga (și șterge) foarte ușor o sarcină oricărui angajat. Și, de asemenea, adăugați orice interpret la orice sarcină.

Cere exemple

Să scriem câteva interogări interesante pentru a înțelege mai bine cum funcționează aceste câmpuri ManyToMany. Și funcționează exact așa cum era de așteptat.

În primul rând, vechiul nostru cod va funcționa fără modificări, deoarece directorul avea înainte un câmp de sarcini:

EmployeeTask task1 = new EmployeeTask();
task1.description = "Do Something Important";
session.persist(task1);

EmployeeTask task2 = new EmployeeTask();
task2.description = "Nothing to do";
session.persist(task2);
session.flush();

Employee director = session.find(Employee.class, 4);
director.tasks.add(task1);
director.tasks.add(task2);

session.update(director);
session.flush();

În al doilea rând, dacă vrem să atribuim un alt executant unei anumite sarcini, atunci este și mai ușor să facem acest lucru:

Employee director = session.find(Employee.class, 4);
EmployeeTask task = session.find(EmployeeTask.class, 101);
task.employees.add(director);

session.update(task);
session.flush();

Important! Ca urmare a executării acestei solicitări, nu numai sarcina va avea un executor-director, ci și directorul va avea sarcina nr. 101.

În primul rând, faptul despre relația dintre director și sarcina din tabelul employee_task va fi stocat ca șir: (4,101).

În al doilea rând, câmpurile marcate cu adnotări @ManyToMany sunt obiecte proxy, iar atunci când sunt accesate, este întotdeauna executată o interogare a bazei de date.

Deci, dacă adăugați o sarcină unui angajat și salvați informații despre angajat în baza de date, apoi sarcina va avea un nou executor în lista executorilor.

Comentarii
  • Popular
  • Nou
  • Vechi
Trebuie să fii conectat pentru a lăsa un comentariu
Această pagină nu are încă niciun comentariu