Contenu du cours
Manipulation des Données Java avec Hibernate
Manipulation des Données Java avec Hibernate
Relations d'Entité et Types de Cascade
Relations entre Entités
Comme vous le savez peut-être grâce au cours Bases de Données Relationnelles et Normalisation, en SQL, il existe certaines relations entre les tables. De même, dans Hibernate, une entité peut référencer une autre et vice versa. Celles-ci sont appelées Relations entre Entités. Il existe au total 4 types de ces relations.
Un-à-Un
Ce type de relation indique qu'une entité est associée à une seule autre entité. Par exemple, un pays peut avoir une seule capitale. Si nous représentons cela en termes de tables et d'entités, il y aura une relation Un-à-Un entre elles. Dans Hibernate, pour spécifier une telle relation, nous utilisons l'annotation @OneToOne
:
Si cette annotation est présente au-dessus d'un champ d'entité, alors dans la table correspondante, une colonne de clé étrangère sera créée, qui contiendra une référence à l'identifiant de l'entité associée.
Cela ressemblera à ceci :
Évidemment, dans la table "cities", les capitales seront stockées avec leurs IDs. Par exemple, sous l'ID 4, il y aura "Managua", sous 11, "Washington", et ainsi de suite.
Un-à-Plusieurs
Dans une telle relation, une entité sera associée à plusieurs autres entités. C'est facile à comprendre avec l'exemple d'un réalisateur et de ses films. Un réalisateur peut avoir plusieurs films différents, mais chaque film ne devrait avoir qu'un seul réalisateur.
Remarque
Oui, je sais qu'il y a des cas où plusieurs réalisateurs travaillent sur un film, mais pour notre exemple actuel, ce n'est pas important.
Pour indiquer une telle relation, l'annotation @OneToMany
est placée au-dessus du champ dans la classe d'entité. Le champ doit être une liste :
Si une telle annotation est présente au-dessus du champ, alors une table séparée sera créée dans la base de données, qui contiendra les identifiants de cette entité et de l'entité associée.
Cela ressemblera à ceci :
De tels identifiants feront référence aux deux entités, montrant les relations entre elles.
Many-to-One
Dans ce type, de nombreuses entités sont liées à une seule. Vous pouvez le considérer comme l'inverse de One-to-Many, et vous auriez raison. Pour une meilleure compréhension, considérez l'exemple des relations étudiant-université. De nombreux étudiants peuvent être associés à une seule université.
Pour établir une telle relation, vous devez spécifier l'annotation @ManyToOne
au-dessus du champ qui n'est pas une collection :
Si cette annotation est présente au-dessus du champ de l'entité, la table correspondante recevra une colonne de clé étrangère contenant une référence à l'identifiant de l'entité associée.
Cela ressemblera à ceci :
Cela ressemble à One-to-One, mais ici, différents étudiants peuvent avoir la même référence à l'ID de l'université.
Many-to-Many
Dans ce type de relation, de nombreuses entités sont associées à de nombreuses entités. Vous pouvez penser à cette relation en termes de conducteur et de voiture. Un conducteur peut avoir plusieurs voitures, et une voiture peut avoir plusieurs conducteurs (par exemple, dans un service de taxi).
Dans le code, vous devez placer l'annotation @ManyToMany
au-dessus du champ dans l'entité :
Si cette annotation est présente au-dessus d'un champ de l'entité (qui doit être une collection), alors une table séparée sera créée dans la base de données, qui contient les identifiants de l'entité actuelle et de l'entité associée.
Cela ressemblera à ceci :
Comme vous pouvez le voir, ici, un conducteur peut avoir plusieurs voitures, et de même, une voiture peut avoir plusieurs conducteurs.
Vous avez peut-être remarqué que dans certains cas, nous n'avons besoin de spécifier l'annotation que dans une seule classe, tandis que dans d'autres cas, il est nécessaire de la spécifier dans les deux classes d'entités simultanément. Discutons-en plus en détail.
Relations Unidirectionnelles et Bidirectionnelles
Les relations unidirectionnelles et bidirectionnelles entre les entités indiquent comment elles interagissent.
-
Relations Unidirectionnelles : Ce sont les relations que nous avons discutées auparavant. Dans une telle relation, l'annotation de relation est spécifiée uniquement dans une classe d'entité. En termes simples, la classe qui contient le champ annoté sait qu'elle est liée à une autre classe, tandis que l'autre classe, qui ne contient pas l'annotation, n'est pas consciente de la relation. Ce n'est pas compliqué, mais nous sommes plus intéressés par le deuxième cas ;
-
Relations Bidirectionnelles : Dans ce type de relation, la Classe
A
est consciente de sa relation avec la ClasseB
, tout comme la ClasseB
est consciente de sa relation avec la ClasseA
. L'annotation de relation sera spécifiée dans les deux classes d'entités, et cela ressemblera à ceci :
A
B
@Entity public class A { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private int id; private String name; @OneToMany private List<B> list; }
L'interaction entre ces deux entités se produit dans les deux sens.
Pour assurer la cohérence lors des opérations de données, nous devons définir le "côté propriétaire" et le "côté inverse." Le côté propriétaire est le côté qui contrôle la relation, et les mises à jour de la relation dans la base de données ne se produisent qu'après que des modifications ont été apportées du côté propriétaire.
Pour déterminer le côté propriétaire, nous utilisons un paramètre dans l'annotation de la relation. Le paramètre mappedBy
utilise le nom du champ dans l'entité que nous voulons rendre propriétaire. En d'autres termes, nous utilisons cette annotation dans l'entité que nous voulons rendre le côté inverse.
Pour un exemple plus clair, faisons de la classe B
le côté propriétaire :
Maintenant, l'entité B
est devenue le côté propriétaire dans ces relations bidirectionnelles. Les données seront écrites dans la base de données uniquement lorsque des modifications sont apportées dans la classe B
.
Cela est fait pour éviter de créer une nouvelle table contenant les identifiants de ces deux entités. Maintenant, à la place, une colonne séparée, "A_id
" sera ajoutée à la table de l'entité B
, où les identifiants de la relation seront spécifiés.
Il est considéré comme une bonne pratique de faire du côté "ManyToOne" le côté propriétaire. Pour les relations bidirectionnelles "Many-to-Many", l'un ou l'autre côté peut être désigné comme le côté propriétaire.
Plus tard, nous utiliserons cela en pratique, et vous comprendrez mieux comment cela fonctionne et pourquoi c'est nécessaire.
Types de Fetch
Dans les relations d'entités, différents types de fetch sont utilisés. Il y en a deux, discutons de chacun :
-
EAGER signifie que toutes les données nécessaires sont récupérées en une seule requête. Lorsque nous récupérons une entité de la base de données qui a des champs
@OneToOne
ou@ManyToOne
, nous obtenons également des informations complètes sur ces entités liées. Cela signifie qu'avec une requête à la base de données, nous récupérons immédiatement des informations de deux tables, ce qui consomme des ressources, de la mémoire et du temps d'exécution. Un tel type de fetch a un impact significatif sur l'optimisation ; -
LAZY signifie que les données sont récupérées de la base de données uniquement lorsqu'elles sont réellement nécessaires pour une opération spécifique. Lorsque nous récupérons une entité de la base de données qui a des champs
@OneToMany
ou@ManyToMany
, nous n'obtenons pas d'informations sur ces entités liées. C'est un avantage significatif car nous ne récupérons pas d'informations inutiles de la base de données, seulement les données dont nous avons besoin. Cela signifie que seule la table à laquelle nous faisons référence est affectée, sans toucher les tables associées à cette entité.
Note
Se souvenir de cela est assez simple : Les relations se terminant par
Many
auront un type de fetch LAZY ; le reste sera EAGER.
Il est recommandé d'utiliser toujours le type de fetch LAZY, car cela rend le code plus optimisé. Vous pouvez le faire en utilisant la commande fetch = FetchType.LAZY
dans les paramètres de l'annotation.
Voici à quoi cela ressemble dans le code :
Maintenant, lors de l'exécution d'une requête pour cette entité, nous ne l'affecterons que, et n'accéderons à d'autres données que si nécessaire.
Types de Cascade
Maintenant que nous avons appris à interagir uniquement avec l'entité avec laquelle nous travaillons, apprenons comment spécifier les champs spécifiques sur lesquels l'interaction doit se propager lors de la mise en relation des entités dans tous les cas. Les cascades nous aideront avec cela.
Remarque
Ces cascades font partie de la bibliothèque JPA ( Jakarta Persistence API ), qui est la classe parente de Hibernate. Ainsi, dans Hibernate, nous pouvons également utiliser ces classes.
Il existe au total 4 types :
PERSIST
— l'appel de la méthodepersist()
est propagé aux entités liées ;MERGE
— l'appel de la méthodemerge()
est propagé aux entités liées ;REMOVE
— l'appel de la méthoderemove()
est propagé aux entités liées ;ALL
- toutes les opérations mentionnées ci-dessus sont propagées aux entités liées.
Chacun de ces types de cascade a son utilité.
Imaginons une situation où nous enregistrons un nouvel étudiant, et nous voulons que des modifications soient également apportées à la table "universités", par exemple, augmenter le nombre d'étudiants.
Voici comment nous pouvons implémenter cela :
Dans ce cas, lors de la réalisation de modifications en utilisant la méthode persist()
sur l'entité Student, des modifications seront également apportées à l'entité University.
Attribution d'un Département à un Employé
Pour l'instant, la théorie effrayante de ce chapitre est terminée.
Maintenant, rappelons-nous le projet de gestion des employés sur lequel nous travaillons et réfléchissons à la relation entre les entités Employee
et Department
.
Il est évident qu'un employé ne peut travailler que dans un seul département, tandis que de nombreux employés peuvent travailler dans un seul département. Dans ce cas, nous pouvons conclure qu'une relation One-to-Many sera établie du côté de l'Employee
et une relation Many-to-One du côté du Department
.
Établissons une relation unidirectionnelle dans ces tables.
Pour y parvenir, nous ajouterons l'annotation @ManyToOne(fetch = FetchType.LAZY)
, et utiliserons également la nouvelle annotation JoinColumn(name = "department_id")
, pour éviter de créer une nouvelle table.
Oui, cette approche a aussi sa place, car les relations bidirectionnelles sont assez complexes à comprendre et à mettre en œuvre, et de cette façon, nous obtiendrons les avantages des relations bidirectionnelles sans tracas inutiles.
L'entité Employee
ressemblera à ceci :
Aucun autre changement n'est nécessaire, car Hibernate s'occupera de tout pour nous. Maintenant, utilisons la méthode getAll()
implémentée dans le chapitre précédent pour voir les changements :
1. Quel type de relation est le mieux représenté par un réalisateur et ses films ?
2. Dans une relation Plusieurs-à-Un, où est créée la colonne de clé étrangère ?
3. Que signifie l'annotation @ManyToMany
dans Hibernate ?
4. Quel type de fetch est recommandé pour optimiser le code dans Hibernate ?
5. Lors de la définition d'une relation Many-to-One dans Hibernate, quelle annotation est utilisée pour spécifier le nom de la colonne dans la base de données ?
Merci pour vos commentaires !