Relation un-à-un dans JPA

1. Introduction

Dans ce didacticiel, nous examinerons différentes manières de créer des mappages un-à-un dans JPA.

Nous aurons besoin d'une compréhension de base du framework Hibernate, veuillez donc consulter notre Guide d'Hibernate 5 avec Spring pour plus d'informations.

2. Description

Supposons que nous construisions un système de gestion des utilisateurs et que notre patron nous demande de stocker une adresse postale pour chaque utilisateur. Un utilisateur aura une adresse postale et une adresse postale n'aura qu'un seul utilisateur lié.

Ceci est un exemple de relation un-à-un, dans ce cas entre les entités utilisateur et adresse .

Voyons comment nous pouvons l'implémenter dans les sections suivantes.

3. Utilisation d'une clé étrangère

3.1. Modélisation avec une clé étrangère

Jetons un coup d'œil au diagramme ER suivant qui représente un mappage un-à-un basé sur une clé étrangère:

Dans cet exemple, la colonne address_id dans users est la clé étrangère à adresser .

3.2. Implémentation avec une clé étrangère dans JPA

Tout d'abord, créons la classe User et annotons-la de manière appropriée:

@Entity @Table(name = "users") public class User { @Id @GeneratedValue(strategy = GenerationType.AUTO) @Column(name = "id") private Long id; //... @OneToOne(cascade = CascadeType.ALL) @JoinColumn(name = "address_id", referencedColumnName = "id") private Address address; // ... getters and setters } 

Notez que nous plaçons l' annotation @OneToOne sur le champ d'entité associé, Address .

De plus, nous devons placer l' annotation @JoinColumn pour configurer le nom de la colonne dans la table des utilisateurs qui correspond à la clé primaire dans la table des adresses . Si nous ne fournissons pas de nom, Hibernate suivra certaines règles pour en sélectionner un par défaut.

Enfin, notez dans l'entité suivante que nous n'utiliserons pas l' annotation @JoinColumn ici. C'est parce que nous n'en avons besoin que du côté propriétaire de la relation de clé étrangère. En termes simples, celui qui possède la colonne de clé étrangère obtient l' annotation @JoinColumn .

L' entité Address s'avère un peu plus simple:

@Entity @Table(name = "address") public class Address { @Id @GeneratedValue(strategy = GenerationType.AUTO) @Column(name = "id") private Long id; //... @OneToOne(mappedBy = "address") private User user; //... getters and setters }

Nous devons également placer l' annotation @OneToOne ici également. C'est parce qu'il s'agit d'une relation bidirectionnelle. Le côté adresse de la relation est appelé le côté non propriétaire .

4. Utilisation d'une clé primaire partagée

4.1. Modélisation avec une clé primaire partagée

Dans cette stratégie, au lieu de créer une nouvelle colonne address_id, nous marquerons la clé primairecolonne (user_id) de la table d'adresses comme clé étrangère de la table users :

Nous avons optimisé l'espace de stockage en utilisant le fait que ces entités ont une relation un à un entre elles.

4.2. Implémentation avec une clé primaire partagée dans JPA

Notez que nos définitions ne changent que légèrement:

@Entity @Table(name = "users") public class User { @Id @GeneratedValue(strategy = GenerationType.AUTO) @Column(name = "id") private Long id; //... @OneToOne(mappedBy = "user", cascade = CascadeType.ALL) @PrimaryKeyJoinColumn private Address address; //... getters and setters }
@Entity @Table(name = "address") public class Address { @Id @Column(name = "user_id") private Long id; //... @OneToOne @MapsId @JoinColumn(name = "user_id") private User user; //... getters and setters } 

L' attribut mappedBy est maintenant déplacé vers la classe User car la clé étrangère est maintenant présente dans la table d' adresses . Nous avons également ajouté l' annotation @PrimaryKeyJoinColumn , qui indique que la clé primaire de l' entité User est utilisée comme valeur de clé étrangère pour l' entité Address associée .

Nous devons toujours définir un champ @Id dans la classe Address , mais notez que cela fait référence à la colonne user_id et qu'il n'utilise plus l' annotation @GeneratedValue . De plus, sur le champ qui référence l' utilisateur , nous avons ajouté l' annotation @MapsId , qui indique que les valeurs de clé primaire seront copiées à partir de l' entité User .

5. Utilisation d'une table de jointure

Les mappages un-à-un peuvent être de deux types: facultatifs et obligatoires . Jusqu'à présent, nous n'avons vu que des relations obligatoires.

Maintenant, imaginons que nos employés soient associés à un poste de travail. C'est un à un, mais parfois un employé peut ne pas avoir de poste de travail et vice-versa.

5.1. Modélisation avec une table de jointure

Les stratégies dont nous avons discuté jusqu'à présent nous obligent à mettre des valeurs nulles dans la colonne pour gérer les relations optionnelles .

En règle générale, nous pensons aux relations plusieurs-à-plusieurs lorsque nous considérons une table de jointure, mais, en utilisant une table de jointure, dans ce cas, peut nous aider à éliminer ces valeurs nulles:

Maintenant, chaque fois que nous avons une relation, nous allons faire une entrée dans la table emp_workstation et éviter les valeurs nullestout à fait.

5.2. Implémentation avec une table de jointure dans JPA

Notre premier exemple a utilisé @JoinColumn. Cette fois, nous utiliserons @JoinTable :

@Entity @Table(name = "employee") public class Employee { @Id @GeneratedValue(strategy = GenerationType.AUTO) @Column(name = "id") private Long id; //... @OneToOne(cascade = CascadeType.ALL) @JoinTable(name = "emp_workstation", joinColumns = { @JoinColumn(name = "employee_id", referencedColumnName = "id") }, inverseJoinColumns = { @JoinColumn(name = "workstation_id", referencedColumnName = "id") }) private WorkStation workStation; //... getters and setters }
@Entity @Table(name = "workstation") public class WorkStation { @Id @GeneratedValue(strategy = GenerationType.AUTO) @Column(name = "id") private Long id; //... @OneToOne(mappedBy = "workStation") private Employee employee; //... getters and setters }

@ JoinTable indique à Hibernate d'utiliser la stratégie de table de jointure tout en maintenant la relation.

De plus, Employee est le propriétaire de cette relation car nous avons choisi d'y utiliser l'annotation de table de jointure.

6. Conclusion

Dans ce didacticiel, nous avons appris différentes manières de maintenir une association un-à-un dans JPA et Hibernate et quand les utiliser.

Le code source de ce tutoriel se trouve à l'adresse over sur GitHub.