2.1 Conectarea la nivel de tabel

Am văzut cum Hibernate stochează colecțiile în tabele auxiliare. Acum să ne dăm seama cum să organizăm relațiile între tabele cu drepturi depline care stochează clase reale de Entități.

Există patru tipuri de relații între clasele de Entități din Hibernate:

  • unu -la- unu
  • unu -la- mulți
  • multi -la- unu
  • multi -la- multi

Și vom începe analiza cu cea mai simplă opțiune - mai multe -la- unu .

Ați întâlnit deja o astfel de relație între tabele în SQL. Iată cum arată de obicei:

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

masa angajatului:

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 sarcini , care conține sarcini pentru angajați:

id card de identitate al angajatului 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 doar 4 coloane:

  • id – numărul unic al sarcinii (și rândurile din tabel);
  • employee_id – ID-ul angajatului din tabelul de angajați căruia îi este atribuită sarcina;
  • nume – numele și descrierea sarcinii;
  • termen - timpul până la care sarcina trebuie finalizată.

Vedem că multe rânduri din tabelul de sarcini se pot referi la o singură intrare în tabelul de angajați. O astfel de relație la nivel de tabel se numește multi-la -unu.

2.2 Relația cu nivelul clasei Java

Pe lângă comunicarea la nivel de masă, puteți organiza și comunicarea la nivelul claselor de Entități din Hibernate. Acest lucru se face cu o adnotare @ManyToOne.

Dar mai întâi, să creăm două clase: Employee și EmployeeTask :


@Entity
@Table(name="employee")
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 o a doua clasă pentru stocarea locurilor de muncă ale angajaților:


@Entity
@Table(name="task")
class EmployeeTask {
   @Column(name="id")
   public Integer id;
 
   @Column(name="name")
   public String description;
 
  @Column(name="employee_id")
   public Integer employeeId;
 
   @Column(name="deadline")
   public Date deadline;
}

Totul este bine cu aceste clase, dar nu există nicio relație între ele care să reflecte faptul că câmpul employeeId al clasei EmployeeTask se referă la câmpul id al clasei Employee. Este timpul să o reparăm

2.3 Adnotare @ManyToOne.

În primul rând, în Java suntem obișnuiți să operam pe obiecte (și referințe la obiecte) mai degrabă decât pe id-ul lor. Deci, mai întâi de toate, în loc de câmpul employeeId din clasa EmployeeTask, să indicam doar un obiect de tip Employee. Iată cum va arăta noua noastră clasă:


@Entity
@Table(name="task")
class EmployeeTask {
   @Column(name="id")
   public Integer id;
 
   @Column(name="name")
   public String description;
 
   @ManyToOne
   @JoinColumn(name = "employee_id")
   public Employee employee;
 
   @Column(name="deadline")
   public Date deadline;
}

Cu ajutorul adnotării @ManyToOne , am indicat că multe obiecte EmployeeTask se pot referi la un obiect de tip Employee. De asemenea, folosind adnotarea @JoinColumn , am indicat în ce coloană a tabelului nostru este stocat id-ul obiectului Employee.

2.4 Exemple de solicitare

Și acum să arătăm câteva exemple despre modul în care Hibernate poate funcționa cu astfel de clase înrudite.

Scenariul unu

Să scriem o interogare pentru a afla toate sarcinile care au fost atribuite unui anumit utilizator. Iată cum ar arăta această interogare în HQL:

from EmployeeTask where employee.name = "Ivan Ivanovich"

Vă puteți referi pur și simplu la câmpurile claselor dependente printr-un punct. Este foarte confortabil. Dar să scriem în continuare această interogare sub formă de cod Java:


String hql = "from EmployeeTask where employee.name = :username";
Query<EmployeeTask> query = session.createQuery( hql, EmployeeTask.class);
query.setParameter("username", "Ivan Ivanovich");
List<EmployeeTask> resultLIst = query.list();

Scenariul doi

Să scriem o interogare care returnează o listă de angajați care au sarcini restante. O sarcină este întârziată dacă termenul limită este în trecut. Iată cum ar arăta acea interogare în SQL:


SELECT DISTINCT employee.*
FROM task JOIN employee ON task.employee_id = employee.id
WHERE task.deadline < CURDATE();

DISTINCTeste utilizat deoarece pot fi multe sarcini atribuite unui singur utilizator.

Și acum să scriem aceeași interogare în HQL:

select distinct employee from EmployeeTask where deadline < CURDATE();

Angajat din această interogare este un câmp al clasei EmployeeTask

Situația trei

Atribuiți directorului toate sarcinile nealocate. Interogarea SQL va arăta astfel:


UPDATE task SET employee_id = 4 WHERE employee_id IS NULL

Și acum să scriem aceeași interogare în HQL:

update EmployeeTask set employee = :user where employee is null

Ultima interogare este cea mai grea. Trebuie să transmitem ID-ul directorului, dar clasa EmployeeTask nu conține un câmp în care să putem scrie un id, în schimb conține un câmp Employee unde trebuie să atribuim o referință unui obiect de tip Employee.


Employee director = session.get(Employee.class, 4);
 
String hql = "update EmployeeTask set employee = :user where employee is null";
Query<EmployeeTask> query = session.createQuery(hql, EmployeeTask.class);
query.setParameter("user", director);
query.executeUpdate();