Quelle est cette propriété spring.jpa.open-in-view = true dans Spring Boot?

121

J'ai vu une spring.jpa.open-in-view=truepropriété dans la documentation de Spring Boot pour la configuration JPA.

  • La truevaleur par défaut de cette propriété est-elle si elle n'est pas du tout fournie ?;
  • Qu'est-ce que cela fait vraiment? Je n'ai trouvé aucune bonne explication à cela;
  • Est-ce que ça vous fait utiliser SessionFactoryau lieu de EntityManagerFactory? Si oui, comment puis-je lui dire de me permettre d'utiliser à la EntityManagerFactoryplace?

Merci!

Carlos Alberto
la source

Réponses:

52

Cette propriété enregistrera un OpenEntityManagerInViewInterceptor, qui enregistre un EntityManagerdans le thread actuel, vous aurez donc le même EntityManagerjusqu'à ce que la requête Web soit terminée. Cela n'a rien à voir avec un Hibernate, SessionFactoryetc.

Dunni
la source
Pour le moment, j'ai le filtre OpenEntityManagerInViewFilter pour contrôler EntityManager jusqu'à ce que la requête Web soit terminée. Cet intercepteur que vous vouliez dire "OpenEntityManagerInViewInterceptor" est le même que "OpenEntityManagerInViewFilter"? Quelle est la différence entre eux? Alors, je n'aurais plus ce filtre dans mon contexte de servlet pour Spring Boot?
Carlos Alberto
1
L'intercepteur ne fonctionne que lorsque vous utilisez le DispatcherServlet dans Spring (car l'intercepteur est un mécanisme Spring). Le filtre peut être mappé à tous les servlets configurés (nous l'utilisons pour le FacesServlet dans l'une de nos applications). Donc, si vous n'utilisez que DispatcherServlet, vous pouvez ajouter la propriété et supprimer le filtre, sinon utilisez le filtre.
dunni
300

L'anti-modèle OSIV

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, OSIV (Open Session in View) force le contexte de persistance à rester ouvert afin que la couche de vue puisse déclencher l'initialisation du proxy, comme illustré par le schéma suivant.

entrez la description de l'image ici

  • L' OpenSessionInViewFilterappelle la openSessionméthode du sous-jacent SessionFactoryet obtient un nouveau Session.
  • Le Sessionest lié au TransactionSynchronizationManager.
  • Les OpenSessionInViewFilterappels doFilterde la javax.servlet.FilterChainréférence d'objet et la demande est ensuite traitée
  • Le DispatcherServletest appelé et achemine la requête HTTP vers le sous-jacent PostController.
  • L' PostControllerappel le PostServicepour obtenir une liste d' Postentités.
  • Le PostServiceouvre une nouvelle transaction et HibernateTransactionManagerréutilise la même Sessionqui a été ouverte par le OpenSessionInViewFilter.
  • Le PostDAOrécupère la liste des Postentités sans initialiser aucune association paresseuse.
  • Le PostServicevalide la transaction sous-jacente, mais Sessionn'est pas fermé car il a été ouvert en externe.
  • Le DispatcherServletcommence le rendu de l'interface utilisateur, qui, à son tour, parcourt les associations différées et déclenche leur initialisation.
  • Le OpenSessionInViewFilterpeut fermer le Session, 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 Connectioncomme é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 @BatchSizede récupérer des associations par lots et FetchMode.SUBSELECTde 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 est maintenue tout au long de la phase de rendu de l'interface utilisateur, 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 à 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.

Spring Boot et OSIV

Malheureusement, OSIV (Open Session in View) est activé par défaut dans Spring Boot , et OSIV est vraiment une mauvaise idée du point de vue des performances et de l'évolutivité .

Donc, assurez-vous que dans le application.propertiesfichier de configuration, vous avez l'entrée suivante:

spring.jpa.open-in-view=false

Cela désactivera OSIV afin que vous puissiez gérer LazyInitializationExceptionla bonne manière .

À partir de la version 2.0, Spring Boot émet un avertissement lorsque OSIV est activé par défaut, afin que vous puissiez découvrir ce problème bien avant qu'il n'affecte un système de production.

Pour plus de détails sur OSIV, consultez cet article .

Vlad Mihalcea
la source
14
Un AVERTISSEMENT est actuellement enregistré.
Vlad Mihalcea
Cela s'applique-t-il à Spring en général ou uniquement à Spring Boot? Cela peut-il être désactivé via une classe annotée @ Configuration au lieu de définir une propriété?
Gordon
2
Cela ne s'applique qu'à Spring Boot. Dans Spring standard, vous choisissez explicitement les beans à utiliser ou si vous souhaitez un filtre Web, comme OSIV. Je ne sais pas si vous pouvez le désactiver via une annotation. Je ne connais que le paramètre de configuration.
Vlad Mihalcea
Ce n'est pas un anti-modèle. Cela a des impacts sur les performances, parfois négatifs, souvent assez neutres, et de manière positive dans de nombreux cas: si vous voulez réellement une relation paresseuse pour commencer, vous n'avez pas besoin de faire la requête dans tous les cas et peut l'éviter en cas de besoin en utilisant open-in-view.
ymajoros le
5
Selon Wikipédia, "un anti-pattern est une réponse courante à un problème récurrent qui est généralement inefficace et qui risque d'être hautement contre-productif". C'est exactement ce qu'est Open Session in View.
Vlad Mihalcea le