eadl:bloc3:dev_av:td2

Différences

Ci-dessous, les différences entre deux révisions de la page.

Lien vers cette vue comparative

Prochaine révision
Révision précédente
eadl:bloc3:dev_av:td2 [2025/10/07 23:33] – créée jcheroneadl:bloc3:dev_av:td2 [2025/10/08 00:18] (Version actuelle) – [4.4 Optimisation des identifiants avec Tsid] jcheron
Ligne 1: Ligne 1:
-# Séance 2 - JPA Avancé et Optimisation (4h)+====== 2 - JPA Avancé et Optimisation ======
  
-Contexte : fil rouge e-commerce :+Séance 2 (4h)
  
-##  Objectifs pédagogiques+Contexte : fil rouge e-commerce
  
-- Maîtriser les associations bidirectionnelles et leurs pièges +===== Objectifs pédagogiques =====
-- Comprendre et résoudre les problèmes N+1 +
-- Utiliser l'héritage JPA à bon escient +
-- Optimiser les requêtes avec fetch strategies et projections+
  
----+  * Maîtriser les associations bidirectionnelles et leurs pièges 
 +  * Comprendre et résoudre les problèmes N+1 
 +  * Utiliser l'héritage JPA à bon escient 
 +  * Optimiser les requêtes avec fetch strategies et projections
  
-##  Contenu de la séance+===== Partie 1 : Associations JPA (1h30) =====
  
-### **Partie 1 : Associations JPA (1h30)** +==== 1.1 Implémentation des associations manquantes ====
- +
-#### 1.1 Implémentation des associations manquantes+
  
 <WRAP round bloc todo> <WRAP round bloc todo>
 **À réaliser :** **À réaliser :**
-Compléter `Order↔ `OrderItem(bidirectionnel) +  * Compléter ''Order'' ↔ ''OrderItem'' (bidirectionnel) 
-Implémenter `Order→ `User(unidirectionnel) +  Implémenter ''Order'' → ''User'' (unidirectionnel) 
-Gérer `User↔ `Category(preferences,Many-to-Many) +  Gérer ''User'' ↔ ''Category'' (preferences, Many-to-Many) 
-Ajouter `@JsonIgnore`@JsonManagedReferencepour éviter les boucles+  Ajouter ''@JsonIgnore'' ''@JsonManagedReference'' pour éviter les boucles
 </WRAP> </WRAP>
  
 **Points d'attention :** **Points d'attention :**
-Choix du côté propriétaire (`mappedBy`+  * Choix du côté propriétaire (''mappedBy''
-Cascade types appropriés +  Cascade types appropriés 
-Orphan removal +  Orphan removal 
-Lazy vs Eager loading+  Lazy vs Eager loading
  
-#### 1.2 Exercice pratique : Orders & OrderItems+==== 1.2 Exercice pratique : Orders & OrderItems ====
  
-```java+<sxh java;gutter:false>
 // Contraintes métier à implémenter // Contraintes métier à implémenter
 - Un Order doit toujours avoir au moins 1 OrderItem - Un Order doit toujours avoir au moins 1 OrderItem
Ligne 40: Ligne 38:
 - totalAmount calculé automatiquement - totalAmount calculé automatiquement
 - Gestion du stock produit lors de la création - Gestion du stock produit lors de la création
-```+</sxh>
  
 **Tests attendus :** **Tests attendus :**
-Création d'une commande avec items +  * Création d'une commande avec items 
-Calcul automatique du total +  Calcul automatique du total 
-Mise à jour du stock +  Mise à jour du stock 
-Suppression en cascade+  Suppression en cascade
  
----+===== Partie 2 : Problèmes de performance (1h30) =====
  
-### **Partie 2 : Problèmes de performance (1h30)** +==== 2.1 Diagnostic du problème N+1 ====
- +
-#### 2.1 Diagnostic du problème N+1+
  
 <WRAP round bloc important> <WRAP round bloc important>
 **Scénario :** **Scénario :**
-```java+<sxh;gutter:false>
 GET /users/{id}/orders GET /users/{id}/orders
 // Retourne les commandes avec leurs items et produits // Retourne les commandes avec leurs items et produits
-```+</sxh>
  
 **Mission :** **Mission :**
-1. Activer les logs SQL (`spring.jpa.show-sql=true`+  - Activer les logs SQL (''spring.jpa.show-sql=true''
-2. Identifier le problème N+1 +  Identifier le problème N+1 
-3. Compter le nombre de requêtes générées+  Compter le nombre de requêtes générées
 </WRAP> </WRAP>
  
-#### 2.2 Solutions d'optimisation+==== 2.2 Solutions d'optimisation ====
  
 **À implémenter et comparer :** **À implémenter et comparer :**
  
-Solution Cas d'usage Avantages Inconvénients | +Solution Cas d'usage Avantages Inconvénients ^ 
-|----------|-------------|-----------|---------------| +''@EntityGraph'' | Requêtes standards | Simple | Moins flexible | 
-| `@EntityGraph| Requêtes standards | Simple | Moins flexible | +''JOIN FETCH'' | Requêtes complexes | Contrôle total | Code JPQL | 
-`JOIN FETCH| Requêtes complexes | Contrôle total | Code JPQL | +''@BatchSize'' | Lazy loading | Transparent | Moins optimal |
-`@BatchSize| Lazy loading | Transparent | Moins optimal |+
 | DTO Projection | Lecture seule | Performances max | Plus de code | | DTO Projection | Lecture seule | Performances max | Plus de code |
  
 **Exercices :** **Exercices :**
-1. Optimiser `/users/{id}/ordersavec JOIN FETCH +  - Optimiser ''/users/{id}/orders'' avec JOIN FETCH 
-2. Créer une projection pour `/products(liste) +  Créer une projection pour ''/products'' (liste) 
-3. Comparer les performances avant/après+  Comparer les performances avant/après
  
----+===== Partie 3 : Héritage JPA (1h) =====
  
-### **Partie 3 : Héritage JPA (1h)** +==== 3.1 Cas d'usage : Typologie de produits ====
- +
-#### 3.1 Cas d'usage : Typologie de produits+
  
 <WRAP round bloc idea> <WRAP round bloc idea>
Ligne 93: Ligne 86:
  
 Différencier 3 types de produits : Différencier 3 types de produits :
-**PhysicalProduct** : poids, dimensions, frais de port +  * **PhysicalProduct** : poids, dimensions, frais de port 
-**DigitalProduct** : taille fichier, URL download, format +  **DigitalProduct** : taille fichier, URL download, format 
-**ServiceProduct** : durée, date prestation+  **ServiceProduct** : durée, date prestation
  
 Tous partagent : id, name, price, stock, category Tous partagent : id, name, price, stock, category
 </WRAP> </WRAP>
  
-#### 3.2 Implémentation avec stratégies d'héritage+==== 3.2 Implémentation avec stratégies d'héritage ====
  
 **À explorer (au choix ou comparaison) :** **À explorer (au choix ou comparaison) :**
  
-```java+<sxh java;gutter:false>
 // Option 1 : SINGLE_TABLE (par défaut) // Option 1 : SINGLE_TABLE (par défaut)
 @Inheritance(strategy = InheritanceType.SINGLE_TABLE) @Inheritance(strategy = InheritanceType.SINGLE_TABLE)
Ligne 114: Ligne 107:
 // Option 3 : TABLE_PER_CLASS // Option 3 : TABLE_PER_CLASS
 @Inheritance(strategy = InheritanceType.TABLE_PER_CLASS) @Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
-```+</sxh>
  
 **Exercice comparatif :** **Exercice comparatif :**
-Schéma base de données généré +  * Schéma base de données généré 
-Requêtes SQL produites +  Requêtes SQL produites 
-Avantages/inconvénients de chaque stratégie+  Avantages/inconvénients de chaque stratégie
  
-#### 3.3 Requêtes polymorphiques+==== 3.3 Requêtes polymorphiques ====
  
-```java+<sxh java;gutter:false>
 // Repository // Repository
 List<Product> findAll(); // Tous types confondus List<Product> findAll(); // Tous types confondus
 List<PhysicalProduct> findPhysicalProducts(); List<PhysicalProduct> findPhysicalProducts();
  
-// Nouveau endpoint+// Nouveaux endpoints
 GET /products?type=PHYSICAL GET /products?type=PHYSICAL
 GET /products?type=DIGITAL GET /products?type=DIGITAL
-```+</sxh>
  
----+===== Partie 4 : Hypersistence Utils Outils avancés (30min-1h) =====
  
-## 🎯 Livrables attendus+==== 4.1 Introduction à Hypersistence Utils ==== 
 + 
 +<WRAP round bloc info> 
 +**Hypersistence Utils** est une bibliothèque créée par Vlad Mihalcea qui apporte : 
 +  * Des types personnalisés (JSON, Array, etc.) 
 +  * Des utilitaires de diagnostic de performance 
 +  * Des listeners pour optimiser les opérations 
 +  * Des identifiants optimisés (Tsid) 
 +</WRAP> 
 + 
 +=== Dépendance Maven === 
 + 
 +<sxh xml;gutter:false> 
 +<dependency> 
 +    <groupId>io.hypersistence</groupId> 
 +    <artifactId>hypersistence-utils-hibernate-63</artifactId> 
 +    <version>3.7.0</version> 
 +</dependency> 
 +</sxh> 
 + 
 +==== 4.2 Détection automatique des problèmes N+1 ==== 
 + 
 +<WRAP round bloc important> 
 +**Objectif :** Détecter automatiquement les problèmes de performance sans analyse manuelle des logs 
 +</WRAP> 
 + 
 +=== Configuration === 
 + 
 +<sxh bash;gutter:false> 
 +application.properties - Ajout pour Hypersistence 
 + 
 +Détection des problèmes N+1 
 +logging.level.io.hypersistence.utils=DEBUG 
 + 
 +# Limites d'alerte (optionnel) 
 +hypersistence.query.fail.on.pagination.over.collection.fetch=false 
 +</sxh> 
 + 
 +=== Utilisation du QueryStackTraceLogger === 
 + 
 +<sxh java;gutter:false> 
 +// Configuration globale (classe @Configuration) 
 +@Configuration 
 +public class HypersistenceConfiguration { 
 +     
 +    @Bean 
 +    public QueryStackTraceLogger queryStackTraceLogger() { 
 +        return new QueryStackTraceLogger(); 
 +    } 
 +     
 +    @EventListener 
 +    public void onApplicationEvent(ApplicationReadyEvent event) { 
 +        // Active la détection des problèmes N+1 
 +        QueryStackTraceLogger.INSTANCE.setThreshold(10); // Alerte si > 10 requêtes 
 +    } 
 +
 +</sxh> 
 + 
 +**Exercice :**  
 +  * Activer le logger sur l'endpoint ''/users/{id}/orders'' 
 +  * Observer les alertes automatiques 
 +  * Corriger les problèmes détectés 
 + 
 +==== 4.3 Types JSON natifs ====
  
 <WRAP round bloc todo> <WRAP round bloc todo>
-### Priorités (4h)+**Cas d'usage :** Stocker des métadonnées flexibles sur les produits 
 +</WRAP>
  
-**Must have :** +=== Exemple Attributs dynamiques produit ===
-1. ✅ Associations Order/OrderItem/User complètes avec tests +
-2. ✅ Résolution problème N+1 sur au moins 2 endpoints +
-3. ✅ Implémentation héritage produits (1 stratégie au choix) +
-4. ✅ Tests d'intégration validant les performances+
  
-**Nice to have :** +<sxh java;gutter:false> 
-- Comparaison des 3 stratégies d'héritage +@Entity 
-- DTO Projections avec MapStruct +@Table(name = "products"
-- Benchmark avant/après optimisations +public class Product { 
-- Documentation des choix architecturaux+    // ... attributs existants 
 +     
 +    @Type(JsonType.class) 
 +    @Column(columnDefinition = "json"
 +    private Map<String, Object> attributes; 
 +     
 +    // Pour PhysicalProduct : {"weight": 2.5, "dimensions": "30x20x10"
 +    // Pour DigitalProduct : {"fileSize": "1.2GB", "format": "PDF"
 +
 +</sxh> 
 + 
 +=== Données exemple === 
 + 
 +<sxh json;gutter:false> 
 +
 +  "id": "550e8400-e29b-41d4-a716-446655440020", 
 +  "name": "iPhone 15 Pro", 
 +  "price": 1199.99, 
 +  "stock": 25, 
 +  "categoryId": "550e8400-e29b-41d4-a716-446655440010", 
 +  "attributes":
 +    "color": "Titanium Blue", 
 +    "storage": "256GB", 
 +    "warranty": "2 years" 
 +  } 
 +
 +</sxh> 
 + 
 +**Exercice :** 
 +  * Ajouter le champ ''attributes'' à ''Product'
 +  * Créer un endpoint ''GET /products/{id}/attributes'' 
 +  * Filtrer les produits par attribut : ''GET /products?attr.color=Blue'' 
 + 
 +==== 4.4 Optimisation des identifiants avec Tsid ==== 
 + 
 +<WRAP round bloc info> 
 +**Tsid (Time-Sorted Identifiers)** : 
 +  * Alternative performante aux UUID 
 +  * Triables chronologiquement 
 +  * Plus compacts (Long au lieu de UUID) 
 +  * Meilleure performance en base
 </WRAP> </WRAP>
  
----+=== Comparaison UUID vs Tsid ===
  
-##  Critères d'évaluation+<sxh java;gutter:false> 
 +// Avant (UUID) 
 +@Id 
 +@GeneratedValue(strategy = GenerationType.UUID) 
 +private UUID id;
  
-| Critère | Points | +// Après (Tsid) Pour nouvelles entités 
-|---------|--------| +@Id 
-| Associations correctement mappées | 25% | +@TsidGenerator 
-| Résolution problèmes N+1 | 30% | +private Long id; 
-| Implémentation héritage | 25% | +</sxh>
-| Tests et qualité code | 20% |+
  
----+**Exercice optionnel :** 
 +  * Créer une nouvelle entité ''Review'' avec Tsid 
 +  * Comparer les performances d'insertion (benchmark)
  
-## 🔧 Configuration supplémentaire+<html><div class="imageB"></html> 
 +<uml> 
 +@startuml Review Domain Model
  
-```yaml +class Review { 
-# application.yml pour la séance +  id Long 
-spring+  - rating Integer 
-  jpa+  title String 
-    show-sqltrue +  - comment String 
-    properties+  - verified Boolean 
-      hibernate+  - helpfulCount Integer 
-        format_sql: true +  - createdAt LocalDateTime 
-        use_sql_comments: true +  - updatedAt LocalDateTime 
-        generate_statistics: true # Pour mesurer les perfs +}
-logging+
-  level+
-    org.hibernate.statDEBUG # Statistiques Hibernate +
-```+
  
----+class Product { 
 +  id : UUID 
 +  name : String 
 +  price : BigDecimal 
 +  - stock : Integer 
 +}
  
-##  Ressources+class User { 
 +  - id : UUID 
 +  - username : String 
 +  - email : String 
 +}
  
-- [Hibernate Performance Best Practices](https://vladmihalcea.com/tutorials/hibernate/) +Product "1" -- "0..*" Review : product 
-- [Spring Data JPA Query Methods](https://docs.spring.io/spring-data/jpa/docs/current/reference/html/#jpa.query-methods)+User "1" -- "0..*" Review : author 
 + 
 +note right of Review 
 +  Contraintes métier : 
 +  • rating ∈ [1..5] 
 +  • 1 review max par (user, product) 
 +  • verified = true si achat confirmé 
 +  • helpfulCount >= 0 
 +   
 +  Tsid Generator pour l'id 
 +  (performance + tri chronologique) 
 +end note 
 + 
 +@enduml 
 + 
 + 
 + 
 +</uml> 
 +<html></div></html> 
 + 
 +==== 4.5 Monitoring des requêtes en temps réel ==== 
 + 
 +=== DataSourceProxyBeanPostProcessor === 
 + 
 +<sxh java;gutter:false> 
 +@Configuration 
 +public class DataSourceProxyConfiguration { 
 +     
 +    @Bean 
 +    public DataSourceProxyBeanPostProcessor dataSourceProxyBeanPostProcessor() { 
 +        return new DataSourceProxyBeanPostProcessor() { 
 +            @Override 
 +            protected DataSourceProxy createDataSourceProxy(DataSource dataSource) { 
 +                return new DataSourceProxy(dataSource, new QueryCountHolder()); 
 +            } 
 +        }; 
 +    } 
 +
 +</sxh> 
 + 
 +**Exercice :** 
 +  * Mettre en place le monitoring 
 +  * Créer un test d'intégration qui vérifie le nombre exact de requêtes 
 +  * Exemple : ''assertQueryCount(3)'' après un appel API 
 + 
 +==== 4.6 Exercice intégratif ==== 
 + 
 +<WRAP round bloc todo> 
 +**Mission :** Améliorer l'endpoint recommendations 
 + 
 +<sxh;gutter:false> 
 +GET /users/{id}/recommendations 
 +</sxh> 
 + 
 +**Avec Hypersistence :** 
 +  * Détecter automatiquement les problèmes N+1 
 +  * Limiter à 5 requêtes maximum (assertion en test) 
 +  * Stocker les préférences utilisateur en JSON 
 +  * Logger les performances de la recommandation 
 + 
 +**Structure JSON recommandée :** 
 +<sxh json;gutter:false> 
 +// User.preferences (JSON) 
 +
 +  "priceRange": {"min": 50, "max": 500}, 
 +  "brands": ["Apple", "Samsung"], 
 +  "excludeCategories": ["550e8400-..."
 +
 +</sxh> 
 +</WRAP> 
 + 
 +===== Configuration complète ===== 
 + 
 +<sxh bash;gutter:false> 
 +# application.properties - Configuration complète Séance 2 
 + 
 +# H2 Database 
 +spring.datasource.url=jdbc:h2:file:./data/ecommerce 
 +spring.datasource.driverClassName=org.h2.Driver 
 +spring.datasource.username=sa 
 +spring.datasource.password= 
 + 
 +# H2 Console 
 +spring.h2.console.enabled=true 
 +spring.h2.console.path=/h2-console 
 + 
 +# JPA/Hibernate 
 +spring.jpa.hibernate.ddl-auto=update 
 +spring.jpa.show-sql=true 
 +spring.jpa.properties.hibernate.format_sql=true 
 +spring.jpa.properties.hibernate.use_sql_comments=true 
 +spring.jpa.properties.hibernate.generate_statistics=true 
 + 
 +# Logging SQL et statistiques 
 +logging.level.org.hibernate.SQL=DEBUG 
 +logging.level.org.hibernate.type.descriptor.sql.BasicBinder=TRACE 
 +logging.level.org.hibernate.stat=DEBUG 
 +logging.level.org.hibernate.orm.jdbc.bind=TRACE 
 + 
 +# Hypersistence Utils 
 +logging.level.io.hypersistence.utils=DEBUG 
 +</sxh> 
 + 
 +===== Livrables attendus ===== 
 + 
 +<WRAP round bloc todo> 
 +=== Priorités (4h) === 
 + 
 +**Must have :** 
 +  * ✅ Associations Order/OrderItem/User complètes avec tests 
 +  * ✅ Résolution problème N+1 sur au moins 2 endpoints 
 +  * ✅ Implémentation héritage produits (1 stratégie au choix) 
 +  * ✅ **Hypersistence : détection automatique N+1 activée** 
 +  * ✅ Tests d'intégration validant les performances 
 + 
 +**Nice to have :** 
 +  * Comparaison des 3 stratégies d'héritage 
 +  * **Type JSON pour attributs dynamiques produits** 
 +  * **Tsid sur une nouvelle entité (Review, Wishlist...)** 
 +  * Benchmark avant/après optimisations avec query count assertions 
 +  * Documentation des choix architecturaux 
 +</WRAP> 
 + 
 +===== Ressources ===== 
 + 
 +  * [[https://vladmihalcea.com/tutorials/hibernate/|Hibernate Performance Best Practices]] 
 +  * [[https://github.com/vladmihalcea/hypersistence-utils|Hypersistence Utils GitHub]] 
 +  * [[https://hypersistence.io/|Documentation officielle Hypersistence]
 +  * [[https://docs.spring.io/spring-data/jpa/docs/current/reference/html/#jpa.query-methods|Spring Data JPA Query Methods]]
  
---- 
  
 <WRAP round bloc info> <WRAP round bloc info>
 **Conseils :** **Conseils :**
-Commencer par les associations avant l'optimisation +  * Commencer par les associations avant l'optimisation 
-Toujours mesurer avant d'optimiser (logs SQL) +  Toujours mesurer avant d'optimiser (logs SQL) 
-L'héritage n'est pas toujours la meilleure solution (composition > héritage) +  L'héritage n'est pas toujours la meilleure solution (composition > héritage) 
-Privilégier `@ManyToOneLAZY par défaut+  Privilégier ''@ManyToOne'' LAZY par défaut
 </WRAP> </WRAP>
 +
  • eadl/bloc3/dev_av/td2.1759872801.txt.gz
  • Dernière modification : il y a 11 heures
  • de jcheron