为什么 View 中的休眠开放会话被视为一种不好的做法?
你使用什么样的替代策略来避免LazyLoadExceptions?
我确实理解,公开会议在以下方面存在问题:
- 在不同 jvm 中运行的分层应用程序
- 事务仅在最后提交,并且很可能您之前会想要结果。
但是,如果您知道应用程序在单个 VM 上运行,为什么不通过使用视图策略中的开放会话来减轻您的痛苦呢?
你使用什么样的替代策略来避免LazyLoadExceptions?
我确实理解,公开会议在以下方面存在问题:
但是,如果您知道应用程序在单个 VM 上运行,为什么不通过使用视图策略中的开放会话来减轻您的痛苦呢?
在视图中打开会话采用一种糟糕的方法来获取数据。它不是让业务层决定如何最好地获取视图层所需的所有关联,而是强制持久性上下文保持打开状态,以便视图层可以触发代理初始化。
OpenSessionInViewFilter
openSession
SessionFactory
Session
事务同步管理器
。Session
OpenSessionInViewFilter
doFilter
javax.servlet.FilterChain
调度程序Servlet
被调用,它将HTTP请求路由到底层的。PostController
PostController
PostService
Post
PostService
HibernateTransactionManager
Session
OpenSessionInViewFilter
PostDAO
Post
PostService
Session
DispatcherServlet
OpenSessionInViewFilter
Session
乍一看,这看起来可能不是一件可怕的事情,但是,一旦你从数据库的角度来看待它,一系列的缺陷就会变得更加明显。
服务层打开和关闭数据库事务,但之后没有显式事务发生。因此,从 UI 呈现阶段发出的每个附加语句都将在自动提交模式下执行。自动提交会给数据库服务器带来压力,因为每个语句都必须将事务日志刷新到磁盘,从而导致数据库端的大量 I/O 流量。一种优化是将 标记为只读,这将允许数据库服务器避免写入事务日志。Connection
不再有关注点分离,因为语句是由服务层和 UI 呈现过程生成的。编写断言正在生成的语句数量的集成测试需要遍历所有层(Web、服务、DAO),同时将应用程序部署在 Web 容器上。即使使用内存数据库(例如HSQLDB)和轻量级Web服务器(例如Jetty),这些集成测试的执行速度也会比分离层并且后端集成测试使用数据库时慢,而前端集成测试则完全模拟服务层。
UI 层仅限于导航关联,而关联又会触发 N+1 查询问题。尽管 Hibernate 提供了用于
批量获取关联的@BatchSize,并且 FetchMode.SUBSELECT
用于处理这种情况,但注释正在影响默认的提取计划,因此它们被应用于每个业务用例。因此,数据访问层查询更合适,因为它可以根据当前用例数据提取要求进行定制。
最后但并非最不重要的一点是,数据库连接可以在整个 UI 呈现阶段保持(具体取决于您的连接释放模式),这会增加连接租用时间,并由于数据库连接池拥塞而限制整体事务吞吐量。保持连接的次数越多,等待从池中获取连接的其他并发请求就越多。
因此,要么连接保持太长时间,要么为单个HTTP请求获取/释放多个连接,从而给底层连接池带来压力并限制可伸缩性。
遗憾的是,默认情况下,在“在视图中打开会话”在 Spring Boot 中处于启用状态。
因此,请确保在配置文件中,您有以下条目:application.properties
spring.jpa.open-in-view=false
这将禁用 OSIV,以便您可以通过在 打开时获取所有需要的关联来以正确的方式处理。LazyInitializationException
EntityManager
因为从性能和理解的角度来看,在视图层中发送可能未初始化的代理(尤其是集合)并从那里触发休眠加载可能会令人不安。
了解:
使用OSIV会“污染”视图层,并关注与数据访问层相关的问题。
视图层不准备处理延迟加载时可能发生的情况,但据推测,数据访问层是。HibernateException
性能:
OSIV倾向于在地毯下拖曳适当的实体加载 - 您往往不会注意到您的集合或实体被懒惰地初始化(也许是N + 1)。更多方便,更少控制。
更新:请参阅The OpenSessionInView antipattern,了解有关此主题的更大讨论。作者列出了三个要点:
- 每个惰性初始化都会给你一个查询,这意味着每个实体将需要 N + 1 个查询,其中 N 是惰性关联的数。如果您的屏幕显示表格数据,则阅读Hibernate的日志是一个很大的暗示,表明您没有按照自己的意愿进行操作
- 这完全击败了分层架构,因为你在表示层中用DB玷污了你的指甲。这是一个概念上的骗局,所以我可以忍受它,但有一个推论
- 最后但并非最不重要的一点是,如果在获取会话时发生异常,它将在页面写入过程中发生:您无法向用户呈现干净的错误页面,您唯一可以做的就是在正文中写入错误消息