Une application reelle manipule des donnees liees : un etudiant appartient a une
filiere, suit des cours, obtient des notes. Voyons comment exprimer ces liens.
1. Les quatre cardinalites
Relation
Exemple
@OneToOne
Un etudiant a une carte d'etudiant
@OneToMany
Une filiere compte plusieurs etudiants
@ManyToOne
Un etudiant appartient a une filiere
@ManyToMany
Un etudiant suit plusieurs cours, un cours accueille plusieurs etudiants
Le reflexe pour choisir : placez-vous du cote de l'entite que vous ecrivez et posez la question
« combien ? ». Un etudiant a une filiere → @ManyToOne cote
etudiant.
2. ManyToOne et OneToMany
@Entity
public class Etudiant {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String nom;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "filiere_id")
private Filiere filiere;
}
@Entity
public class Filiere {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String libelle;
@OneToMany(mappedBy = "filiere", cascade = CascadeType.ALL)
private List<Etudiant> etudiants = new ArrayList<>();
}
Le role de mappedBy
mappedBy = "filiere" signifie : « la relation est deja decrite par le champ
filiere de l'entite Etudiant ». Sans lui, JPA croirait a deux relations distinctes et
creerait une table de liaison inutile.
Le cote qui porte @JoinColumn est le proprietaire : c'est lui qui
detient la cle etrangere en base. Retenez-le, c'est determinant pour l'enregistrement.
3. ManyToMany
@Entity
public class Etudiant {
@ManyToMany
@JoinTable(
name = "inscriptions",
joinColumns = @JoinColumn(name = "etudiant_id"),
inverseJoinColumns = @JoinColumn(name = "cours_id")
)
private Set<Cours> cours = new HashSet<>();
}
@Entity
public class Cours {
@ManyToMany(mappedBy = "cours")
private Set<Etudiant> etudiants = new HashSet<>();
}
Utilisez un Set plutot qu'une List : il interdit les doublons, ce qui
correspond a la realite (on ne s'inscrit pas deux fois au meme cours).
Quand la liaison porte des donnees
Si l'inscription doit memoriser une date ou une note, @ManyToMany ne suffit plus :
creez une entite Inscription avec deux @ManyToOne. C'est le cas le plus
frequent en pratique.
4. Le chargement : LAZY ou EAGER
Mode
Comportement
Defaut pour
LAZY
Charge seulement a l'acces
@OneToMany, @ManyToMany
EAGER
Charge immediatement
@ManyToOne, @OneToOne
Conseil : mettez tout en LAZY, et chargez explicitement ce dont vous avez besoin.
Un EAGER sur une collection peut charger la moitie de la base pour afficher un nom.
5. Le probleme des N+1 requetes
C'est le piege classique de JPA. Ce code semble anodin :
List<Etudiant> etudiants = repository.findAll(); // 1 requete
for (Etudiant e : etudiants) {
System.out.println(e.getFiliere().getLibelle()); // 1 requete par etudiant
}
Avec 100 etudiants, vous executez 101 requetes. La solution : une jointure
explicite.
@Query("SELECT e FROM Etudiant e JOIN FETCH e.filiere")
List<Etudiant> findAllAvecFiliere();
Une seule requete. Activez spring.jpa.show-sql=true et comptez les lignes : c'est le
meilleur moyen de reperer le probleme avant qu'il ne devienne visible en production.
6. La cascade
Type
Effet
PERSIST
Enregistre aussi les enfants
MERGE
Met a jour les enfants
REMOVE
Supprime les enfants
ALL
Tout ce qui precede
Attention a REMOVE et ALL : supprimer une filiere supprimerait tous ses
etudiants. A reserver aux cas ou l'enfant n'a aucun sens sans son parent.
7. La boucle infinie en JSON
Un etudiant reference sa filiere, qui reference ses etudiants, qui referencent leur filiere…
La serialisation JSON tourne alors indefiniment.
La meilleure solution reste cependant le DTO du chapitre 6 : il ne contient que les
champs voulus, et le probleme ne se pose plus du tout.
A retenir
Le cote qui porte @JoinColumn detient la cle etrangere
mappedBy designe le champ de l'autre cote
Preferez LAZY partout, et chargez explicitement
Surveillez les N+1 : JOIN FETCH les resout
Contre les boucles JSON, le DTO est la vraie reponse
Exercices d'application
Exercice 1. Filiere et etudiantsCreez les entites Filiere et Etudiant avec leur relation. Enregistrez une filiere et trois etudiants, puis verifiez la cle etrangere en base.
Exercice 2. Reperer un N+1Activez show-sql=true, listez des etudiants en affichant leur filiere, et comptez les requetes. Corrigez avec JOIN FETCH et recomptez.
Exercice 3. InscriptionsModelisez l'inscription d'etudiants a des cours avec une date d'inscription. Expliquez pourquoi @ManyToMany ne suffit pas.
Cherchez avant de regarder la solution : c'est en butant sur les difficultes que l'on progresse.
Questions / Réponses
Aucune question. Soyez le premier à poser une question.