2.1 Liaison au niveau de la table

Nous avons vu comment Hibernate stocke les collections dans des tables auxiliaires. Voyons maintenant comment organiser les relations entre des tables complètes qui stockent de vraies classes Entity.

Il existe quatre types de relations entre les classes Entity dans Hibernate :

  • un à un
  • un à plusieurs
  • plusieurs -vers- un
  • beaucoup -à- beaucoup

Et nous commencerons l'analyse avec l'option la plus simple - plusieurs vers un .

Vous avez déjà rencontré une telle relation entre les tables en SQL. Voici à quoi cela ressemble généralement :

identifiant nom profession salaire âge join_date
1 Ivanov Ivan Programmeur 100000 25 2012-06-30
2 Petrov Petr Programmeur 80000 23 2013-08-12
3 Ivanov Sergueï Testeur 40000 trente 2014-01-01
4 Rabinovitch Moisha Directeur 200000 35 2015-05-12
5 Kirienko Anastasia Responsable administratif 40000 25 2015-10-10
6 Vaska Chat 1000 3 2018-11-11

tableau des employés :

Ce tableau comporte les colonnes suivantes :

  • identifiant INT
  • nom VARCHAR
  • métier VARCHAR
  • salaire INT
  • âge INT
  • join_date DATE

Et voici à quoi ressemble la table des tâches , qui contient les tâches des employés :

identifiant id_employé nom date limite
1 1 Correction d'un bug sur le frontend 2022-06-01
2 2 Correction d'un bug sur le backend 2022-06-15
3 5 Acheter du café 2022-07-01
4 5 Acheter du café 2022-08-01
5 5 Acheter du café 2022-09-01
6 (NUL) Nettoyer le bureau (NUL)
7 4 Profite de la vie (NUL)
8 6 Profite de la vie (NUL)

Ce tableau n'a que 4 colonnes :

  • id – numéro de tâche unique (et lignes du tableau) ;
  • employee_id – ID de l'employé de la table des employés à laquelle la tâche est affectée ;
  • nom – nom et description de la tâche ;
  • délai - l'heure à laquelle la tâche doit être terminée.

Nous voyons que de nombreuses lignes dans la table des tâches peuvent faire référence à une seule entrée dans la table des employés. Une telle relation au niveau de la table est appelée plusieurs à un.

2.2 Relation avec le niveau de classe Java

En plus de la communication au niveau de la table, vous pouvez également organiser la communication au niveau des classes Entity dans Hibernate. Cela se fait avec une annotation @ManyToOne.

Mais d'abord, créons juste deux classes : Employee et 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;
}

Et une deuxième classe pour stocker les emplois des employés :


@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;
}

Tout va bien avec ces classes, mais il n'y a pas de relation entre elles qui refléterait le fait que le champ employeeId de la classe EmployeeTask fait référence au champ id de la classe Employee. Il est temps de le réparer

2.3 Annotation @ManyToOne.

Premièrement, en Java, nous sommes habitués à opérer sur des objets (et des références d'objets) plutôt que sur leur identifiant. Alors tout d'abord, au lieu du champ employeeId dans la classe EmployeeTask, pointons simplement sur un objet de type Employee. Voici à quoi ressemblera notre nouvelle classe :


@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;
}

A l'aide de l'annotation @ManyToOne , nous avons indiqué que de nombreux objets EmployeeTask peuvent faire référence à un objet de type Employee. De plus, à l'aide de l'annotation @JoinColumn , nous avons indiqué dans quelle colonne de notre table l'id de l'objet Employee est stocké.

2.4 Exemples de demandes

Et maintenant, montrons quelques exemples de la façon dont Hibernate peut fonctionner avec de telles classes liées.

Scénario un

Écrivons une requête pour connaître toutes les tâches qui ont été assignées à un utilisateur spécifique. Voici à quoi ressemblerait cette requête dans HQL :

from EmployeeTask where employee.name = "Ivan Ivanovich"

Vous pouvez simplement faire référence aux champs des classes dépendantes via un point. C'est très confortable. Mais écrivons tout de même cette requête sous forme de code 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();

Scénario deux

Écrivons une requête qui renvoie une liste d'employés qui ont des tâches en retard. Une tâche est en retard si son échéance est dans le passé. Voici à quoi ressemblerait cette requête en SQL :


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

DISTINCTest utilisé car de nombreuses tâches peuvent être affectées à un seul utilisateur.

Et maintenant, écrivons la même requête en HQL :

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

Employé dans cette requête est un champ de la classe EmployeeTask

Cas trois

Attribuez toutes les tâches non assignées au directeur. La requête SQL ressemblera à ceci :


UPDATE task SET employee_id = 4 WHERE employee_id IS NULL

Et maintenant, écrivons la même requête en HQL :

update EmployeeTask set employee = :user where employee is null

La dernière requête est la plus difficile. Nous devons passer l'ID du directeur, mais la classe EmployeeTask ne contient pas de champ où nous pouvons écrire un identifiant, mais contient un champ Employee où nous devons attribuer une référence à un objet de type 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();