Ressource
- https://github.com/Adrien-Courses/R605-TP-JPA-join-vs-joinfetch
- Lancez une premiĂšre fois puis renommez le fichier
data.sqlen.data.sql
Dans de TD nous allons voir la différence entre un JOIN et un JOINFETCH au travers des Spécifications JPA
2. Consignes
Nous avons deux tables Tarif et TarifPeriode.
- Les tarifs sont associés à un article (ici un String)
- et les tarifs ne sont pas uniques, suivant la périodes ils varient
Câest pour cette raison que nous avons une relation âun tarif Ă plusieurs tarifPeriodeâ
- par exemple, le tarif
id=1Ă trois pĂ©riode chacune dâune durĂ©e dâun an
mysql> show tables;
+-----------------------------+
| Tables_in_join-vs-joinfetch |
+-----------------------------+
| tarif |
| tarif_periode |
+-----------------------------+
2 rows in set (0.01 sec)
mysql> select * from tarif_periode;
+----+----------+-------+------------+
| id | fk_tarif | prix | date_debut |
+----+----------+-------+------------+
| 1 | 1 | 8.99 | 2023-01-01 |
| 2 | 1 | 10.99 | 2024-01-01 |
| 3 | 1 | 12.5 | 2025-01-01 |
| 4 | 2 | 8.75 | 2024-01-01 |
+----+----------+-------+------------+
Objectif Nous souhaitons rĂ©cupĂ©rer les Tarifs ayant une TarifPeriode.dateDebut > :dateDebut quâon donne en paramĂštre.
Structure du code
Pour ce faire, les morceaux de code suivants sont fournis
- Le Controller :
http://localhost:8080/tarifs?dateDebut=2023-01-01 - Le Service
- Le Repository
Dans le service on va utiliser une spécification pour créer un filtre sur la date (API Criteria)
public List<Tarif> getTarifsAvecDateDebut(LocalDate dateDebut) {
Specification<Tarif> spec = TarifSpecifications.filter(dateDebut);
return tarifRepository.findAll(spec);
}public class TarifSpecifications {
public static Specification<Tarif> filter(LocalDate dateDebut) {
Specification<Tarif> specification = Specification.where(null);
if(dateDebut != null) {
specification = specification.and(avecTarifPeriodeValide(dateDebut));
}
return specification;
}
private static Specification<Tarif> avecTarifPeriodeValide(LocalDate date) {
return (root, query, cb) -> {
// Créer le join avec TarifPeriode
Join<Tarif, TarifPeriode> periodeJoin = root.join("periodes", JoinType.INNER);
// Filtrer sur la date
return cb.greaterThan(periodeJoin.get("dateDebut").as(LocalDate.class), date);
};
}
}Coder : observer la mĂ©thode avecTarifValide et exĂ©cutez les requĂȘtes suivantes
http://localhost:8080/tarifs?dateDebut=2023-01-01http://localhost:8080/tarifs?dateDebut=2024-01-01
Le résultat vous semble-t-il fonctionnellement juste ? Que remarquez-vous ?
- Analysez les requĂȘtes SQL, pour vous aidez mettez un point dâarrĂȘt ligne 25 dans
TarifService
Utilisation JOIN FETCH
Remplacez le code précédent par
private static Specification<Tarif> avecTarifPeriodeValide(LocalDate date) {
return (root, query, cb) -> {
// Créer le join avec TarifPeriode
Fetch<Tarif, TarifPeriode> fetch = root.fetch("periodes", JoinType.INNER);
Join<Tarif, TarifPeriode> periodeJoin = (Join<Tarif, TarifPeriode>) fetch;
// Filtrer sur la date
return cb.greaterThan(periodeJoin.get("dateDebut").as(LocalDate.class), date);
};
}Le résultat vous semble-t-il fonctionnellement juste ? Que remarquez-vous ?
- Analysez les requĂȘtes SQL
The FETCH keyword of the JOIN FETCH statement is JPA-specific. It tells the persistence provider to not only join the 2 database tables within the query but to also initialize the association on the returned entity. You can use it with a JOIN and a LEFT JOIN statement.
https://thorben-janssen.com/hibernate-tips-difference-join-left-join-fetch-join/