Ressource
Définition
FetchType.LAZY: indique que la relation doit être chargée à la demande ;FetchType.EAGER: indique que la relation doit être chargée en même temps que l’entité qui la porte.
Lorsqu’on travaille avec JPA (Java Persistence API), il est essentiel de comprendre comment sont chargées les relations entre entités pour optimiser les performances de l’application. Deux stratégies principales existent : Lazy Fetching (chargement paresseux) et Eager Fetching (chargement immédiat).
Default fetching
- Par défaut,
@OneToManyet@ManyToManyadopte une approche Lazy Fetching - Par défaut,
@OneToOneet@ManyToOneadapte une approche Eager Fetching
Fetch Lazy
Les relations ne sont pas chargées immédiatement lors de la requête initiale. Elles sont récupérées uniquement lorsqu’elles sont explicitement accédées dans le code.
@Entity
public class Etudiant {
@Id
@GeneratedValue
private Long id;
@OneToMany(mappedBy = "etudiant") // fetch = FetchType.LAZY par défaut
private List<Livre> livresLus;
}Exemple
Dans cet exemple, les livres lus par un étudiant ne seront chargés en mémoire que lorsqu’on accédera à la liste livresLus.
Etudiant etudiant = entityManager.find(Etudiant.class, etudiantId);
List<Livre> livres = etudiant.getLivresLus();Comme LAZY deux requêtes SQL vont être nécessaires
SELECT * FROM etudiant WHERE id = ?;- puis
SELECT * FROM livre WHERE etudiant_id = ?;
Fetch Eager
Le Eager Fetching force le chargement immédiat des relations lors de la requête initiale. Une seule requête avec jointure (JOIN) est exécutée pour récupérer l’étudiant et ses livres lus.
@Entity
public class Etudiant {
@Id
@GeneratedValue
private Long id;
@OneToMany(mappedBy = "etudiant", fetch = FetchType.EAGER) // Chargement immédiat
private List<Livre> livresLus;
}Exemple
Etudiant etudiant = entityManager.find(Etudiant.class, etudiantId);
List<Livre> livres = etudiant.getLivresLus();Cette fois lorsqu’on récupère l’étudiant on récupère également l’ensemble des livres lus, donc une seule requête SQL
SELECT e.*, l.*
FROM etudiant e
LEFT JOIN livre l ON e.id = l.etudiant_id
WHERE e.id = ?;
Quel mode choisir ?
Et bien ça dépend.
- Dans le cas où l’on sait que la relation livres sera explorée systématiquement après la lecture d’un étudiant, il serait plus malin de n’émettre qu’un seul SELECT, avec une jointure, de manière à peupler la relation livres à l’avance.
- ⇒ Cela ne ferait qu’un seul aller-retour avec la base de données, et serait de ce fait beaucoup plus performant.
- En revanche, dans le cas d’une relation qui, pour des raisons applicatives, ne serait pas explorée, ou rarement, alors l’exécution de la jointure lors du SELECT serait un surcoût inutile.
Problème du n+1
En complément, nous allons mettre un nom sur cette problématique.
The N+1 query problem happens when the data access framework executed N additional SQL statements to fetch the same data that could have been retrieved when executing the primary SQL query (JOIN)

Supposons la relation une Voiture à plusieurs Roue ⇒ @OneToMany
- Supposons maintenant qu’il faille parcourir toutes les voitures et, pour chacune d’entre elles, afficher la liste des roues. L’implémentation O/R naïve ferait ce qui suit
SELECT * FROM Cars;- Puis pour chaque voiture
SELECT * FROM Wheel WHERE CarId = ?
En d’autres termes, nous avons une sélection pour les voitures, puis N sélections supplémentaires, où N est le nombre total de voitures. Par exemple, une jointure aurait été pertinente et plus performante
SELECT c.*, w.*
FROM Cars c
LEFT JOIN Wheel w ON c.id = w.car_id;
Plusieurs solutions existent, nous en étudierons dans le TP3 JPA Fetching