• Aucun résultat trouvé

Solutions possibles pour la résolution du problème N+1 requêtes

4.4 Problématique des N+1 requêtes

4.4.2 Solutions possibles pour la résolution du problème N+1 requêtes

Hibernate offre la possibilité de résoudre le problème du N+1 requêtes décrit ci dessus. Ces so- lutions se basent essentiellement sur le choix de la stratégie de chargement des données ainsi que la stratégie de mise en cache.

4.4.2.1 Solutions basées sur le mode de chargement

1. Le chargement par lot : Une première solution est le mode de chargement par lot. En effet, pour l’exemple cité ci dessous, si l’on fixe le lot à charger à 10 par exemple, cela réduirait le problème de N+1 requêtes à N/10 +1 requêtes.

2. Le chargement par jointure : Une deuxième alternative à la résolution du problème serait d’activer le chargement par jointure, ce qui revient, nous le rappelons, à l’exécution d’une jointure externe pour récupérer la collection. Ceci résout bien le problème du N+1 requêtes, étant donnée qu’une seule requête est issue. Toutefois, toute transaction ultérieur devrait charger automatiquement la collection, ce qui pourrait entraîner au chargement de données qui ne soit pas nécessaire et donc dégrader les performances du système. C’est pour cette raison que le mode de chargement par jointure n’est pas celui définit par défaut au sein d’Hibernate.

3. La déclaration du mode de chargement au niveau du code à travers les "criteria" : Cette solution consiste à définir des critères propres à la session afin d’indiquer le mode de charge- ment. Ce paramétrage ne se fait plus au sein de la déclaration des collections, mais au moment d’invoquer la requête et donc au niveau de la logique métier. Ceci a l’avantage de garder le mode lazy par défaut au sein de la collection et de forcer le mode de chargement au mode de jointure externe au niveau du code, au moment voulu.

4.4.2.2 Stratégies de mise en cache

Un des avantages des applications utilisant une couche de persistance objet-relationnelle par rap- port à celles utilisant JDBC est la possibilité du recours à la mise en cache.

Ceci est bien évidement notable dans les applications à caractère à conserver une grande quantité de métadonnées dans la base de données. C’est aussi le cas pour celles qui incluent principalement des opérations de lecture, auquel cas, il est préconisé d’avoir recours au cache pour pouvoir y stocker les données et mimimser les échanges avec la bases de données. En effet, le cache sert principalement à conserver une représentation de l’état courant de la base de données proche de l’application. Le cache constituerait donc une image ou une copie locale des données qui se situe entre l’application et la base de données.

Il est à noter que l’utilisation du cache à l’exécution se produit :

• lorsqu’il s’agit de récupérer une instance d’une entité particulière avec un id particulier, • lorsque la couche de persistance résout une association de manière tardive et essais de récupérer

une collection appartenant à une entité particulière.

La gestion du cache peut s’avérer coûteuse en terme de ressources et donc en terme de temps serveur, surtout s’il est manipulé en écriture. Il serait judicieux alors de mettre en cache les données de référence, de paramétrage et celles rarement mises à jour par l’application mais fréquemment consultées. Les codes postaux, les pays ou les articles d’un catalogue qui ne seraient mis à jour que trimestriellement, constituent de bons exemples de données à mettre en cache.

Hibernate dispose d’un cache de premier niveau, un cache de second niveau et donne la possibilité de sauvegarder les résultats des requêtes à travers le cache de requête.

4.4.2.3 Le cache de premier niveau

Le cache de premier niveau est le cache utilisé par défaut dans hibernate. Il est spécifique à une session. Son utilisation évite les échanges inutiles avec la base de données. A chaque fois ou les opérations de recherche, de mise à jour ou d’accés (save(), update(), saveOrUpdate(), load(), find(), list(),iterate() ou filter()) sont invoquées sur un objet, ce dernier sera ajouté au cache de premier niveau. L’appel à la méthode flush() permet la synchronisation de l’état de cet objet avec la base de données. Il serait utile dans certains cas d’avoir recours à l’annulation de cette synchronisation ou à la suppression de l’objet et de ses collections du cache, ceci est possible par l’appel de la méthode evict(). Si toutefois on souhaite évincer complètement tous les objets du cache de session, il suffit alors de faire appel à la méthode Session.clear().

4.4.2.4 Le cache de second niveau

Le cache de second niveau possède la portée d’une SessionFactory. Toutes les sessions partagent le même cache de second niveau. Ce type de cache est à activer pour des classes définies ou même des collections données. Les divers types de données requièrent différentes stratégies de cache : la pro- portion de lectures et d’écritures varie, la taille des tables de base de données varie et certaines tables

sont partagées avec d’autres applications externes. Le cache de second niveau est donc configurable au niveau de granularité d’une classe ou d’une collection.

La configuration du cache se résume à la définition de certains paramètres à mettre en place à savoir :

• activation/désactivation du cache ; • stratégie de concurrence d’Hibernate ; • stratégies d’expiration du cache ;

• le format physique du cache (mémoire, fichiers indexés, réplication du cluster).

Les stratégies de concurrence du cache ont pour rôle de définir les stratégies de stockage et de récupération des données dans le cache. Ce rôle est important, car il définit également la sémantique d’isolation des transactions pour cet élément particulier. En activant le cache de second niveau, il faut décider, pour chaque classe persistante, la stratégie de concurrence de cache à utiliser. Il existe quatre stratégies de concurrence :

Lecture seule. Utilisé en cas de lecture uniquement sans aucune modification des instances d’une classe. C’est la stratégie la plus simple et la plus performante

Lecture/Ecriture. Utilisé lorsque l’application a besoin de mettre à jour les données

Lecture/Ecriture non stricte. Utilisé lorsque l’application a besoin de mettre à jour les données mais occasionnellement. En d’autres terme, l’utiliser quand il est peu probable que deux tran- sactions essaient de mettre à jour le même élément simultanément et qu’une isolation transac- tionnelle stricte ne soit pas nécessaire.

Transactionnelle. Cette stratégie est à utilisée dans le cas où l’on a besoin d’une isolation complète des transaction.

Par ailleurs, ils existent plusieurs fournisseurs de cache compatibles avec Hibernate, telle que EHCache, OSCache, Hashtable, SwarmCache et JBOssTreeCache. On optera pour l’un plutôt que l’autre en considérant des paramètres tel que l’utilisation ou pas de cluster, le support de cache de requête, le support des stratégies de cache.

4.4.2.5 Le cache de requête

Hibernate implémente également un cache pour stocker les résultats de requêtes. Ce cache s’in- tègre étroitement avec le cache de second niveau. Par défaut, les résultats des requêtes ne sont pas mises en cache, et il est nécessaire donc de l’activer pour pouvoir l’utiliser. Il est à noter que le cache de requête ne conserve pas la globalité des objets, il met en cache uniquement les valeurs des identi- fiants et les valeurs des types de base. Le cache de requête doit toujours être utilisé avec le cache de second niveau pour pourvoir fonctionner.