Et quel genre de stratégies alternatives utilisez-vous pour éviter les LazyLoadExceptions?
Je comprends que la session ouverte en vue a des problèmes avec:
- Applications en couches s'exécutant dans différents jvm
- Les transactions ne sont validées qu'à la fin, et vous aimeriez probablement les résultats avant.
Mais, si vous savez que votre application s'exécute sur une seule machine virtuelle, pourquoi ne pas vous soulager en utilisant une stratégie de session ouverte en vue?
Réponses:
Parce que l'envoi de proxies éventuellement non initialisés, en particulier des collections, dans la couche de vue et le déclenchement du chargement de mise en veille prolongée à partir de là peut être gênant du point de vue des performances et de la compréhension.
Comprendre :
L'utilisation d'OSIV «pollue» la couche de vue avec des préoccupations liées à la couche d'accès aux données.
La couche de vue n'est pas prête à gérer ce
HibernateException
qui peut se produire lors d'un chargement différé, mais vraisemblablement la couche d'accès aux données l'est.Performance :
OSIV a tendance à tirer le bon chargement des entités sous le tapis - vous avez tendance à ne pas remarquer que vos collections ou entités sont initialisées paresseusement (peut-être N + 1). Plus de commodité, moins de contrôle.
Mise à jour: voir L'anti-modèle OpenSessionInView pour une discussion plus large sur ce sujet. L'auteur énumère trois points importants:
la source
Pour une description plus longue, vous pouvez lire mon article Open Session In View Anti-Pattern . Sinon, voici un résumé des raisons pour lesquelles vous ne devriez pas utiliser Open Session In View.
Open Session In View adopte une mauvaise approche pour récupérer des données. Au lieu de laisser la couche de gestion décider de la meilleure manière d'extraire toutes les associations nécessaires à la couche de vue, elle force le contexte de persistance à rester ouvert afin que la couche de vue puisse déclencher l'initialisation du proxy.
OpenSessionInViewFilter
appelle laopenSession
méthode du sous-jacentSessionFactory
et obtient un nouveauSession
.Session
est lié auTransactionSynchronizationManager
.OpenSessionInViewFilter
appelsdoFilter
de lajavax.servlet.FilterChain
référence d'objet et la demande est ensuite traitéeDispatcherServlet
est appelé et achemine la requête HTTP vers le sous-jacentPostController
.PostController
appel lePostService
pour obtenir une liste d'Post
entités.PostService
ouvre une nouvelle transaction etHibernateTransactionManager
réutilise la mêmeSession
qui a été ouverte par leOpenSessionInViewFilter
.PostDAO
récupère la liste desPost
entités sans initialiser aucune association paresseuse.PostService
valide la transaction sous-jacente, maisSession
n'est pas fermé car il a été ouvert en externe.DispatcherServlet
commence le rendu de l'interface utilisateur, qui, à son tour, parcourt les associations différées et déclenche leur initialisation.OpenSessionInViewFilter
peut fermer leSession
, et la connexion à la base de données sous-jacente est également libérée.À première vue, cela ne semble pas être une chose terrible à faire, mais, une fois que vous l'avez vue du point de vue d'une base de données, une série de failles commencent à devenir plus évidentes.
La couche de service ouvre et ferme une transaction de base de données, mais par la suite, aucune transaction explicite n'est en cours. Pour cette raison, chaque instruction supplémentaire émise à partir de la phase de rendu de l'interface utilisateur est exécutée en mode de validation automatique. L'auto-validation met la pression sur le serveur de base de données car chaque instruction doit vider le journal des transactions sur le disque, ce qui entraîne un trafic d'E / S important côté base de données. Une optimisation serait de marquer le
Connection
comme étant en lecture seule, ce qui permettrait au serveur de base de données d'éviter d'écrire dans le journal des transactions.Il n'y a plus de séparation des préoccupations car les instructions sont générées à la fois par la couche de service et par le processus de rendu de l'interface utilisateur. L'écriture de tests d'intégration affirmant le nombre d'instructions générées nécessite de passer par toutes les couches (web, service, DAO), tout en déployant l'application sur un conteneur web. Même lors de l'utilisation d'une base de données en mémoire (par exemple HSQLDB) et d'un serveur Web léger (par exemple Jetty), ces tests d'intégration seront plus lents à exécuter que si les couches étaient séparées et que les tests d'intégration back-end utilisaient la base de données, tandis que le les tests d'intégration frontale se moquaient complètement de la couche de service.
La couche d'interface utilisateur est limitée à la navigation dans les associations qui peuvent, à leur tour, déclencher des problèmes de requête N + 1. Bien qu'Hibernate propose
@BatchSize
de récupérer les associations par lots etFetchMode.SUBSELECT
de faire face à ce scénario, les annotations affectent le plan de récupération par défaut, elles sont donc appliquées à chaque cas d'utilisation métier. Pour cette raison, une requête de couche d'accès aux données est beaucoup plus appropriée car elle peut être adaptée aux exigences de récupération de données du cas d'utilisation actuel.Enfin, la connexion à la base de données peut être maintenue tout au long de la phase de rendu de l'interface utilisateur (en fonction du mode de libération de votre connexion), ce qui augmente la durée du bail de connexion et limite le débit global des transactions en raison de la congestion du pool de connexions de la base de données. Plus la connexion est maintenue, plus d'autres requêtes simultanées vont attendre pour obtenir une connexion à partir du pool.
Donc, soit vous maintenez la connexion pendant trop longtemps, soit vous acquérez / libérez plusieurs connexions pour une seule requête HTTP, ce qui exerce une pression sur le pool de connexions sous-jacent et limite l'évolutivité.
Botte de printemps
Malheureusement, Open Session in View est activé par défaut dans Spring Boot .
Donc, assurez-vous que dans le
application.properties
fichier de configuration, vous avez l'entrée suivante:Cela désactivera OSIV, afin que vous puissiez gérer
LazyInitializationException
la bonne manière .la source
les transactions peuvent être validées dans la couche service - les transactions ne sont pas liées à OSIV. C'est le
Session
qui reste ouvert, pas une transaction - en cours.si vos couches d'application sont réparties sur plusieurs machines, vous ne pouvez pratiquement pas utiliser OSIV - vous devez initialiser tout ce dont vous avez besoin avant d'envoyer l'objet sur le fil.
OSIV est un moyen agréable et transparent (c'est-à-dire qu'aucun de votre code n'est conscient que cela se produit) pour utiliser les avantages en termes de performances du chargement paresseux
la source
Je ne dirais pas qu'une session ouverte en vue est considérée comme une mauvaise pratique; qu'est-ce qui vous donne cette impression?
Open-Session-In-View est une approche simple de la gestion des sessions avec Hibernate. Parce que c'est simple, c'est parfois simpliste. Si vous avez besoin d'un contrôle précis sur vos transactions, comme avoir plusieurs transactions dans une demande, Open-Session-In-View n'est pas toujours une bonne approche.
Comme d'autres l'ont souligné, il y a des compromis à faire avec OSIV - vous êtes beaucoup plus sujet au problème N + 1 parce que vous êtes moins susceptible de réaliser les transactions que vous lancez. En même temps, cela signifie que vous n'avez pas besoin de modifier votre couche de service pour vous adapter aux modifications mineures de votre vue.
la source
Si vous utilisez un conteneur d'inversion de contrôle (IoC) tel que Spring, vous voudrez peut-être vous renseigner sur la portée du bean . Essentiellement, je dis à Spring de me donner un
Session
objet Hibernate dont le cycle de vie couvre toute la requête (c'est-à-dire qu'il est créé et détruit au début et à la fin de la requête HTTP). Je n'ai pas à me soucier deLazyLoadException
s ni de fermer la session puisque le conteneur IoC gère cela pour moi.Comme mentionné, vous devrez penser aux problèmes de performances de N + 1 SELECT. Vous pouvez toujours configurer votre entité Hibernate par la suite pour effectuer un chargement de jointure hâtive dans les endroits où les performances sont un problème.
La solution de cadrage du bean n'est pas spécifique à Spring. Je sais que PicoContainer offre la même capacité et je suis sûr que d'autres conteneurs IoC matures offrent quelque chose de similaire.
la source
D'après ma propre expérience, OSIV n'est pas si mal. Le seul arrangement que j'ai fait utilise deux transactions différentes: - la première, ouverte dans la "couche service", où j'ai la "logique métier" - la seconde ouverte juste avant le rendu de la vue
la source
Je viens de publier un article sur les règles d'utilisation de la session ouverte dans mon blog. Vérifiez-le si vous êtes intéressé.
http://heapdump.wordpress.com/2010/04/04/should-i-use-open-session-in-view/
la source
Je suis v. Rusty sur Hibernate .. mais je pense qu'il est possible d'avoir plusieurs transactions dans une session Hibernate. Ainsi, vos limites de transaction ne doivent pas être les mêmes que les événements de démarrage / arrêt de session.
OSIV, imo, est principalement utile car nous pouvons éviter d'écrire du code pour démarrer un 'contexte de persistance' (aka session) chaque fois que la requête doit faire un accès à la base de données.
Dans votre couche de service, vous devrez probablement faire des appels à des méthodes qui ont des besoins de transaction différents, tels que «Requis, Nouveau requis, etc.» La seule chose dont ces méthodes ont besoin est que quelqu'un (c'est-à-dire le filtre OSIV) ait démarré le contexte de persistance, de sorte que la seule chose dont ils doivent s'inquiéter est - "Hé, donnez-moi la session de mise en veille prolongée pour ce fil .. Je dois faire un peu Trucs DB ".
la source
Cela n'aidera pas trop mais vous pouvez vérifier mon sujet ici: * Hibernate Cache1 OutOfMemory avec OpenSessionInView
J'ai des problèmes avec OutOfMemory à cause d'OpenSessionInView et de nombreuses entités chargées, car elles restent dans le cache Hibernate level1 et ne sont pas collectées par la mémoire (je charge beaucoup d'entités avec 500 éléments par page, mais toutes les entités restent dans le cache)
la source