为什么 Hibernate Open Session in View 被认为是一种不好的做法? [英] Why is Hibernate Open Session in View considered a bad practice?

查看:27
本文介绍了为什么 Hibernate Open Session in View 被认为是一种不好的做法?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

你使用什么样的替代策略来避免 LazyLoadExceptions?

And what kind of alternative strategies do you use for avoiding LazyLoadExceptions?

我知道视图中的打开会话存在以下问题:

I do understand that open session in view has issues with:

  • 在不同的 jvm 中运行的分层应用程序
  • 事务仅在最后提交,而且您很可能希望之前的结果.

但是,如果您知道您的应用程序在单个虚拟机上运行,​​为什么不通过在视图中使用开放会话策略来减轻您的痛苦呢?

But, if you know that your application is running on a single vm, why not ease your pain by using an open session in view strategy?

推荐答案

在视图中打开会话采用了一种糟糕的方法来获取数据.它不是让业务层决定如何最好地获取视图层所需的所有关联,而是强制持久化上下文保持打开状态,以便视图层可以触发代理初始化.

Open Session In View takes a bad approach to fetching data. Instead of letting the business layer decide how it’s best to fetch all the associations that are needed by the View layer, it forces the Persistence Context to stay open so that the View layer can trigger the Proxy initialization.

  • OpenSessionInViewFilter调用底层SessionFactoryopenSession方法,获取新的Session.
  • Session 绑定到 TransactionSynchronizationManager.
  • OpenSessionInViewFilter 调用javax.servlet.FilterChain 对象引用的doFilter 并进一步处理请求
  • DispatcherServlet 被调用,它将 HTTP 请求路由到底层 PostController.
  • PostController 调用 PostService 以获取 Post 实体的列表.
  • PostService 打开一个新事务,HibernateTransactionManager 重用 OpenSessionInViewFilter 打开的同一个 Session>.
  • PostDAO 获取 Post 实体列表,无需初始化任何惰性关联.
  • PostService 提交底层事务,但 Session 没有关闭,因为它是在外部打开的.
  • DispatcherServlet 开始呈现 UI,进而导航惰性关联并触发它们的初始化.
  • OpenSessionInViewFilter可以关闭Session,同时释放底层数据库连接.
  • The OpenSessionInViewFilter calls the openSession method of the underlying SessionFactory and obtains a new Session.
  • The Session is bound to the TransactionSynchronizationManager.
  • The OpenSessionInViewFilter calls the doFilter of the javax.servlet.FilterChain object reference and the request is further processed
  • The DispatcherServlet is called, and it routes the HTTP request to the underlying PostController.
  • The PostController calls the PostService to get a list of Post entities.
  • The PostService opens a new transaction, and the HibernateTransactionManager reuses the same Session that was opened by the OpenSessionInViewFilter.
  • The PostDAO fetches the list of Post entities without initializing any lazy association.
  • The PostService commits the underlying transaction, but the Session is not closed because it was opened externally.
  • The DispatcherServlet starts rendering the UI, which, in turn, navigates the lazy associations and triggers their initialization.
  • The OpenSessionInViewFilter can close the Session, and the underlying database connection is released as well.

乍一看,这可能看起来并不可怕,但是,一旦从数据库的角度来看,一系列缺陷就会变得更加明显.

At a first glance, this might not look like a terrible thing to do, but, once you view it from a database perspective, a series of flaws start to become more obvious.

服务层打开和关闭一个数据库事务,但之后没有显式事务在进行.出于这个原因,从 UI 呈现阶段发出的每个附加语句都在自动提交模式下执行.自动提交给数据库服务器带来压力,因为每个语句都必须将事务日志刷新到磁盘,从而导致数据库端的大量 I/O 流量.一种优化是将 Connection 标记为只读,这将允许数据库服务器避免写入事务日志.

The service layer opens and closes a database transaction, but afterward, there is no explicit transaction going on. For this reason, every additional statement issued from the UI rendering phase is executed in auto-commit mode. Auto-commit puts pressure on the database server because each statement must flush the transaction log to disk, therefore causing a lot of I/O traffic on the database side. One optimization would be to mark the Connection as read-only which would allow the database server to avoid writing to the transaction log.

不再有关注点分离,因为语句是由服务层和 UI 渲染过程生成的.编写断言正在生成的语句数量的集成测试需要遍历所有层(网络、服务、DAO),同时将应用程序部署在网络容器上.即使在使用内存数据库(例如 HSQLDB)和轻量级 Web 服务器(例如 Jetty)时,这些集成测试的执行速度也会比层分离并且后端集成测试使用数据库要慢,而前端集成测试完全是在模拟服务层.

There is no separation of concerns anymore because statements are generated both by the service layer and by the UI rendering process. Writing integration tests that assert the number of statements being generated requires going through all layers (web, service, DAO), while having the application deployed on a web container. Even when using an in-memory database (e.g. HSQLDB) and a lightweight web server (e.g. Jetty), these integration tests are going to be slower to execute than if layers were separated and the back-end integration tests used the database, while the front-end integration tests were mocking the service layer altogether.

UI 层仅限于导航关联,这反过来又会触发 N+1 查询问题.尽管 Hibernate 提供了 @BatchSize 用于批量获取关联,以及 FetchMode.SUBSELECT 为了应对这种情况,注释影响了默认的获取计划,因此它们被应用于每个业务用例.出于这个原因,数据访问层查询更合适,因为它可以针对当前用例数据获取要求进行定制.

The UI layer is limited to navigating associations which can, in turn, trigger N+1 query problems. Although Hibernate offers @BatchSize for fetching associations in batches, and FetchMode.SUBSELECT to cope with this scenario, the annotations are affecting the default fetch plan, so they get applied to every business use case. For this reason, a data access layer query is much more suitable because it can be tailored for the current use case data fetch requirements.

最后但并非最不重要的一点是,数据库连接可以在整个 UI 呈现阶段(取决于您的连接释放模式)保持,这会增加连接租用时间并由于数据库连接池拥塞而限制整体事务吞吐量.保持的连接越多,其他并发请求将等待从池中获取连接的次数就越多.

Last but not least, the database connection could be held throughout the UI rendering phase(depending on your connection release mode) which increases connection lease time and limits the overall transaction throughput due to congestion on the database connection pool. The more the connection is held, the more other concurrent requests are going to wait to get a connection from the pool.

因此,要么连接保持时间过长,要么为单个 HTTP 请求获取/释放多个连接,从而给底层连接池带来压力并限制可扩展性.

So, either you get the connection held for too long, either you acquire/release multiple connections for a single HTTP request, therefore putting pressure on the underlying connection pool and limiting scalability.

不幸的是,在 Spring Boot 中默认启用在视图中打开会话.

因此,请确保在 application.properties 配置文件中,您有以下条目:

So, make sure that in the application.properties configuration file, you have the following entry:

spring.jpa.open-in-view=false

这将禁用 OSIV,以便您可以通过在 EntityManager 打开时获取所有需要的关联以正确的方式处理 LazyInitializationException.

This will disable OSIV, so that you can handle the LazyInitializationException the right way, by fetching all needed association while the EntityManager is open.

这篇关于为什么 Hibernate Open Session in View 被认为是一种不好的做法?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆