Différences
Ci-dessous, les différences entre deux révisions de la page.
Prochaine révision | Révision précédente | ||
eadl:bloc3:dev_av:td2 [2025/10/07 23:33] – créée jcheron | eadl: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 | + | ====== |
- | 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' | + | |
- | - 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' | ||
+ | * 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 | + | * Compléter |
- | - Implémenter | + | |
- | - Gérer | + | |
- | - Ajouter | + | |
</ | </ | ||
**Points d' | **Points d' | ||
- | - Choix du côté propriétaire (`mappedBy`) | + | * Choix du côté propriétaire ('' |
- | - Cascade types appropriés | + | |
- | - Orphan removal | + | |
- | - Lazy vs Eager loading | + | |
- | #### 1.2 Exercice pratique : Orders & OrderItems | + | ==== 1.2 Exercice pratique : Orders & OrderItems |
- | ```java | + | < |
// 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 | ||
- | ``` | + | </ |
**Tests attendus :** | **Tests attendus :** | ||
- | - Création d'une commande avec items | + | * Création d'une commande avec items |
- | - Calcul automatique du total | + | |
- | - Mise à jour du stock | + | |
- | - 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 | + | < |
GET / | GET / | ||
// Retourne les commandes avec leurs items et produits | // Retourne les commandes avec leurs items et produits | ||
- | ``` | + | </ |
**Mission :** | **Mission :** | ||
- | 1. Activer les logs SQL (`spring.jpa.show-sql=true`) | + | - Activer les logs SQL ('' |
- | 2. Identifier le problème N+1 | + | |
- | 3. Compter le nombre de requêtes générées | + | |
</ | </ | ||
- | #### 2.2 Solutions d' | + | ==== 2.2 Solutions d' |
**À implémenter et comparer :** | **À implémenter et comparer :** | ||
- | | Solution | + | ^ Solution |
- | |----------|-------------|-----------|---------------| | + | | '' |
- | | `@EntityGraph` | Requêtes standards | Simple | Moins flexible | | + | | '' |
- | | `JOIN FETCH` | Requêtes complexes | Contrôle total | Code JPQL | | + | | '' |
- | | `@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 | + | - Optimiser |
- | 2. Créer une projection pour `/products` (liste) | + | |
- | 3. Comparer les performances avant/ | + | |
- | --- | + | ===== Partie 3 : Héritage JPA (1h) ===== |
- | ### **Partie 3 : Héritage JPA (1h)** | + | ==== 3.1 Cas d' |
- | + | ||
- | #### 3.1 Cas d' | + | |
<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 | + | |
- | - **ServiceProduct** : durée, date prestation | + | |
Tous partagent : id, name, price, stock, category | Tous partagent : id, name, price, stock, category | ||
</ | </ | ||
- | #### 3.2 Implémentation avec stratégies d' | + | ==== 3.2 Implémentation avec stratégies d' |
**À explorer (au choix ou comparaison) :** | **À explorer (au choix ou comparaison) :** | ||
- | ```java | + | < |
// 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) | ||
- | ``` | + | </ |
**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 | + | |
- | - Avantages/ | + | |
- | #### 3.3 Requêtes polymorphiques | + | ==== 3.3 Requêtes polymorphiques |
- | ```java | + | < |
// Repository | // Repository | ||
List< | List< | ||
List< | List< | ||
- | // Nouveau endpoint | + | // Nouveaux endpoints |
GET / | GET / | ||
GET / | GET / | ||
- | ``` | + | </ |
- | --- | + | ===== 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) | ||
+ | </ | ||
+ | |||
+ | === Dépendance Maven === | ||
+ | |||
+ | <sxh xml; | ||
+ | < | ||
+ | < | ||
+ | < | ||
+ | < | ||
+ | </ | ||
+ | </ | ||
+ | |||
+ | ==== 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 | ||
+ | </ | ||
+ | |||
+ | === Configuration === | ||
+ | |||
+ | <sxh bash; | ||
+ | # application.properties - Ajout pour Hypersistence | ||
+ | |||
+ | # Détection des problèmes N+1 | ||
+ | logging.level.io.hypersistence.utils=DEBUG | ||
+ | |||
+ | # Limites d' | ||
+ | hypersistence.query.fail.on.pagination.over.collection.fetch=false | ||
+ | </ | ||
+ | |||
+ | === Utilisation du QueryStackTraceLogger === | ||
+ | |||
+ | <sxh java; | ||
+ | // 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); | ||
+ | } | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | **Exercice :** | ||
+ | * Activer le logger sur l' | ||
+ | * 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' |
+ | </ | ||
- | **Must have :** | + | === Exemple |
- | 1. ✅ Associations Order/ | + | |
- | 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' | + | |
- | **Nice to have :** | + | <sxh java; |
- | - Comparaison des 3 stratégies d'héritage | + | @Entity |
- | - DTO Projections avec MapStruct | + | @Table(name = " |
- | - Benchmark avant/après optimisations | + | public class Product { |
- | - Documentation | + | // ... attributs existants |
+ | |||
+ | @Type(JsonType.class) | ||
+ | @Column(columnDefinition = " | ||
+ | private Map< | ||
+ | |||
+ | // Pour PhysicalProduct : {" | ||
+ | // Pour DigitalProduct : {" | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | === Données exemple === | ||
+ | |||
+ | <sxh json; | ||
+ | { | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | } | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | **Exercice | ||
+ | * Ajouter le champ '' | ||
+ | * Créer un endpoint '' | ||
+ | * Filtrer les produits par attribut : '' | ||
+ | |||
+ | ==== 4.4 Optimisation | ||
+ | |||
+ | <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 | ||
</ | </ | ||
- | --- | + | === Comparaison UUID vs Tsid === |
- | ## Critères d' | + | <sxh java; |
+ | // Avant (UUID) | ||
+ | @Id | ||
+ | @GeneratedValue(strategy = GenerationType.UUID) | ||
+ | private UUID id; | ||
- | | Critère | Points | | + | // Après (Tsid) |
- | |---------|--------| | + | @Id |
- | | Associations correctement mappées | 25% | | + | @TsidGenerator |
- | | Résolution problèmes N+1 | 30% | | + | private Long id; |
- | | Implémentation héritage | 25% | | + | </ |
- | | Tests et qualité code | 20% | | + | |
- | --- | + | **Exercice optionnel :** |
+ | * Créer une nouvelle entité '' | ||
+ | * Comparer les performances d' | ||
- | ## 🔧 Configuration supplémentaire | + | < |
+ | < | ||
+ | @startuml Review Domain Model | ||
- | ```yaml | + | class Review { |
- | # application.yml | + | - id : Long |
- | spring: | + | |
- | | + | - title : String |
- | show-sql: true | + | - comment |
- | | + | - verified |
- | | + | - helpfulCount |
- | | + | |
- | use_sql_comments: | + | - updatedAt |
- | generate_statistics: | + | } |
- | logging: | + | |
- | | + | |
- | | + | |
- | ``` | + | |
- | --- | + | class Product { |
+ | | ||
+ | | ||
+ | | ||
+ | - stock : Integer | ||
+ | } | ||
- | ## Ressources | + | class User { |
+ | - id : UUID | ||
+ | - username : String | ||
+ | - email : String | ||
+ | } | ||
- | - [Hibernate Performance Best Practices](https:// | + | Product " |
- | - [Spring Data JPA Query Methods](https:// | + | User " |
+ | |||
+ | 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 | ||
+ | |||
+ | |||
+ | |||
+ | </ | ||
+ | < | ||
+ | |||
+ | ==== 4.5 Monitoring des requêtes en temps réel ==== | ||
+ | |||
+ | === DataSourceProxyBeanPostProcessor === | ||
+ | |||
+ | <sxh java; | ||
+ | @Configuration | ||
+ | public class DataSourceProxyConfiguration { | ||
+ | |||
+ | @Bean | ||
+ | public DataSourceProxyBeanPostProcessor dataSourceProxyBeanPostProcessor() { | ||
+ | return new DataSourceProxyBeanPostProcessor() { | ||
+ | @Override | ||
+ | protected DataSourceProxy createDataSourceProxy(DataSource dataSource) { | ||
+ | return new DataSourceProxy(dataSource, | ||
+ | } | ||
+ | }; | ||
+ | } | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | **Exercice :** | ||
+ | * Mettre en place le monitoring | ||
+ | * Créer un test d' | ||
+ | * Exemple : '' | ||
+ | |||
+ | ==== 4.6 Exercice intégratif ==== | ||
+ | |||
+ | <WRAP round bloc todo> | ||
+ | **Mission :** Améliorer l' | ||
+ | |||
+ | < | ||
+ | GET / | ||
+ | </ | ||
+ | |||
+ | **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; | ||
+ | // User.preferences (JSON) | ||
+ | { | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | } | ||
+ | </ | ||
+ | </ | ||
+ | |||
+ | ===== Configuration complète ===== | ||
+ | |||
+ | <sxh bash; | ||
+ | # application.properties - Configuration complète Séance 2 | ||
+ | |||
+ | # H2 Database | ||
+ | spring.datasource.url=jdbc: | ||
+ | spring.datasource.driverClassName=org.h2.Driver | ||
+ | spring.datasource.username=sa | ||
+ | spring.datasource.password= | ||
+ | |||
+ | # H2 Console | ||
+ | spring.h2.console.enabled=true | ||
+ | spring.h2.console.path=/ | ||
+ | |||
+ | # JPA/ | ||
+ | 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 | ||
+ | </ | ||
+ | |||
+ | ===== Livrables attendus ===== | ||
+ | |||
+ | <WRAP round bloc todo> | ||
+ | === Priorités (4h) === | ||
+ | |||
+ | **Must have :** | ||
+ | * ✅ Associations Order/ | ||
+ | * ✅ 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' | ||
+ | |||
+ | **Nice to have :** | ||
+ | * Comparaison des 3 stratégies d' | ||
+ | * **Type JSON pour attributs dynamiques produits** | ||
+ | * **Tsid sur une nouvelle entité (Review, Wishlist...)** | ||
+ | * Benchmark avant/ | ||
+ | * Documentation des choix architecturaux | ||
+ | </ | ||
+ | |||
+ | ===== Ressources ===== | ||
+ | |||
+ | * [[https:// | ||
+ | * [[https:// | ||
+ | * [[https:// | ||
+ | * [[https:// | ||
- | --- | ||
<WRAP round bloc info> | <WRAP round bloc info> | ||
**Conseils :** | **Conseils :** | ||
- | - Commencer par les associations avant l' | + | * Commencer par les associations avant l' |
- | - Toujours mesurer avant d' | + | |
- | - L' | + | |
- | - Privilégier | + | |
</ | </ | ||
+ |