Différences
Ci-dessous, les différences entre deux révisions de la page.
Les deux révisions précédentes Révision précédente Prochaine révision | Révision précédente | ||
eadl:bloc3:dev_av:td2 [2025/10/07 23:40] – [Configuration supplémentaire] jcheron | eadl:bloc3:dev_av:td2 [2025/10/08 00:18] (Version actuelle) – [4.4 Optimisation des identifiants avec Tsid] jcheron | ||
---|---|---|---|
Ligne 126: | Ligne 126: | ||
</ | </ | ||
- | ===== Livrables attendus | + | ===== Partie 4 : Hypersistence Utils - Outils avancés (30min-1h) |
+ | |||
+ | ==== 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 |
- | * ✅ Associations Order/ | + | |
- | * ✅ Résolution problème N+1 sur au moins 2 endpoints | + | |
- | * ✅ Implémentation héritage produits (1 stratégie au choix) | + | |
- | * ✅ 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 des choix architecturaux | + | // ... 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 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 | ||
</ | </ | ||
- | ===== Critères d' | + | === Comparaison UUID vs Tsid === |
- | ^ Critère ^ Points ^ | + | <sxh java; |
- | | Associations correctement mappées | 25% | | + | // Avant (UUID) |
- | | Résolution problèmes N+1 | 30% | | + | @Id |
- | | Implémentation héritage | 25% | | + | @GeneratedValue(strategy = GenerationType.UUID) |
- | | Tests et qualité code | 20% | | + | private UUID id; |
- | ===== Configuration | + | // Après (Tsid) - Pour nouvelles entités |
+ | @Id | ||
+ | @TsidGenerator | ||
+ | private Long id; | ||
+ | </ | ||
+ | |||
+ | **Exercice optionnel :** | ||
+ | * Créer une nouvelle entité '' | ||
+ | * Comparer les performances d' | ||
+ | |||
+ | < | ||
+ | < | ||
+ | @startuml Review Domain Model | ||
+ | |||
+ | class Review { | ||
+ | - id : Long | ||
+ | - rating : Integer | ||
+ | - title : String | ||
+ | - comment : String | ||
+ | - verified : Boolean | ||
+ | - helpfulCount : Integer | ||
+ | - createdAt : LocalDateTime | ||
+ | - updatedAt : LocalDateTime | ||
+ | } | ||
+ | |||
+ | class Product { | ||
+ | - id : UUID | ||
+ | - name : String | ||
+ | - price : BigDecimal | ||
+ | - stock : Integer | ||
+ | } | ||
+ | |||
+ | class User { | ||
+ | - id : UUID | ||
+ | - username : String | ||
+ | - email : String | ||
+ | } | ||
+ | |||
+ | Product " | ||
+ | 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 | ||
- | < | + | < |
- | Voici la configuration en application.properties : | + | # application.properties - Configuration complète |
- | # Application | + | |
# H2 Database | # H2 Database | ||
Ligne 164: | Ligne 375: | ||
spring.datasource.password= | spring.datasource.password= | ||
- | # H2 Console | + | # H2 Console |
spring.h2.console.enabled=true | spring.h2.console.enabled=true | ||
spring.h2.console.path=/ | spring.h2.console.path=/ | ||
Ligne 175: | Ligne 386: | ||
spring.jpa.properties.hibernate.generate_statistics=true | spring.jpa.properties.hibernate.generate_statistics=true | ||
- | # Logging SQL et statistiques | + | # Logging SQL et statistiques |
logging.level.org.hibernate.SQL=DEBUG | logging.level.org.hibernate.SQL=DEBUG | ||
logging.level.org.hibernate.type.descriptor.sql.BasicBinder=TRACE | logging.level.org.hibernate.type.descriptor.sql.BasicBinder=TRACE | ||
logging.level.org.hibernate.stat=DEBUG | logging.level.org.hibernate.stat=DEBUG | ||
- | logging.level.org.hibernate.engine.internal.StatisticalLoggingSessionEventListener=WARN | ||
- | |||
- | # Pour voir les paramètres des requêtes (optionnel mais utile) | ||
logging.level.org.hibernate.orm.jdbc.bind=TRACE | 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 ===== | ===== Ressources ===== | ||
* [[https:// | * [[https:// | ||
+ | * [[https:// | ||
+ | * [[https:// | ||
* [[https:// | * [[https:// | ||
+ | |||
<WRAP round bloc info> | <WRAP round bloc info> |