这里的每个人都是对的,每个人都有自己的方式,因此这里的每个人都是错误的。
“三个人的故事”(A.和B. Strugatsky)
如果使用Spring Data JPA,则在升级到Spring Boot 2之后,启动应用程序时,您可能会在日志中注意到新的警告:
默认情况下,spring.jpa.open-in-view是启用的。 因此,可以在视图渲染期间执行数据库查询。 明确配置spring.jpa.open-in-view以禁用此警告。
在本文中,我将尝试解释其含义,应归咎于谁和该做什么。
要在Spring Boot上引发完整的应用程序,只需要一个@SpringBootApplication
批注。 为了使之成为可能,该框架使用了大量的自动配置和默认设置。 而且,要开箱即用,Spring Boot开发人员必须从几个替代选项中为每个选择一些应用程序开发概念,以便用户无需明确选择它们。 一方面,这有利于快速入门和轻松开发,但另一方面,一段时间后可能会发现某些默认概念/范例/设置不适合该项目,并且需要大量工作来放弃它。 这样的概念之一就是“在视图中打开会话(OSIV)”模式 ,它默认包含在Spring Boot中。
在这种模式下,Hibernate会话在HTTP请求处理期间一直保持打开状态,包括创建View(JSON资源或HTML页面)的阶段。 这使得在业务逻辑层中提交事务之后可以将数据延迟加载到表示层中。 例如,我们从数据库请求商品实体。 该文章应与注释一起显示。 OSIV允许您在呈现HTML时简单地调用getComments()
实体方法,并且注释将在单独的请求中加载。 禁用OSIV模式时,我们将获得LazyInitializationException
,因为会话已经关闭并且Article
实体不再由Hibernate管理。 大多数Hibernate开发人员都遇到过LazyInitializationException
OSIV允许您在处理HTTP请求的任何阶段根据需要加载数据来避免这种情况。
Spring Boot中的OpenEntityManagerInViewInterceptor
是使用OpenEntityManagerInViewInterceptor
Web请求OpenEntityManagerInViewInterceptor
。 与纯Spring不同,此处默认启用它。
OSIV被视为反模式。 最棒的是,Hibernate的开发者之一Vlad Mihalcea解释了他文章的有害方面: “查看反模式的公开会议” 。 重点:
- 没有事务的数据库查询将在自动提交模式下工作,从而使其负担沉重。
- 没有职责分离,任何应用程序层都可以生成SQL查询,这使测试变得困难。
- 将与实体相关的每个集合加载到单独的请求中时,会出现n + 1问题。
- 与数据库的长连接再次增加了数据库的负载并降低了吞吐量。
这些是OSIV模式的非常令人不快的问题,并且似乎强烈反对不使用它。 但是, 在默认情况下禁用它的请求中 ,Spring Boot开发人员也提出了为新项目启用OSIV的充分理由:
- 向后兼容。 由于禁用了OSIV,升级到Spring Boot 2时的现有应用程序可能会遇到错误和错误。 为了避免这种情况,您只需要设置单个值
spring.jpa.open-in-view
,但这也使得切换到新版本变得困难。 - 对于Spring Boot来说,初学者的易用性和快速入门非常重要。 如果OSIV被禁用,那么初学者可能还不清楚为什么在访问实体方法时接收诸如相关元素集合之类的直观期望是行不通的。 而是,用户将收到
LazyInitializationException
,这将减慢其到正在运行的应用程序的路径。 - OSIV使您可以提高代码的简单性,可用性和开发速度。
- 很难找到没有OSIV的情况下如何构建应用程序的简单示例。
- 没有OSIV,业务逻辑层需要知道如何在UI中呈现数据,即,它将需要什么DTO或应该与根实体一起加载哪些相关数据。 再次是没有责任分工。
因此,我们可以在这个问题上区分两种观点。 从数据库架构师(DBA)的角度来看,在视图中打开会话当然是不可接受的,因为应用程序与数据库的交互未得到最佳组织,并增加了负载。 但是,为了提高速度,简化开发和简化学习工具,我们经常使用不太理想的解决方案-我们使用托管语言编写代码,使用文本数据格式进行网络交互等。 从框架开发人员的位置出发,为了简化开发工作并快速入门,OSIV允许您减轻认知负担,即减轻开发应用程序所需的概念数量。 如果开发人员选择JPA,他已经同意降低性能,以换取开发便利。 JPA帮助结交对象和关系数据模型的朋友。 当使用对象样式来获取相关元素时,我们简单地转向实体方法(即使在表示层中),尽管这种简单性在欺骗,但它是简单,逻辑和直观的期望。
在View的Open Session中有很多示例和教程可供使用,整个体系结构很清晰:服务层请求JPA实体,表示层通过中间DTO直接以JSON序列化它,或者使用来自它的数据来呈现HTML页面。
尚不清楚的是如何在没有OSIV的情况下工作。 上面提到的请求中的一位Spring开发人员抱怨这一点,他们对反模式大喊大叫,但是没有简单的例子说明如何不使用它。 在此实施例中,实体只能用于写入和读取多个DTO,这些DTO与UI中的每个数据集是分开的,它们直接从数据库映射。 或描述特定Web查询所需的相关集合的联接的自定义SQL查询。 也就是说,在业务逻辑层中有更多样板和对UI需求的描述。 在禁用OSIV的情况下,JPA抽象开始流行,该应用程序描述了与数据库交互的更多技术细节。
因此,使用OSIV进行开发更加简单。 但是问题是,如果将来您要放弃它,则必须重做很多,通过设置一个属性在项目中使用数据库的概念无法更改。 您可能需要重做整个应用程序体系结构。 但是,放弃OSIV可能是过早的优化,这会减慢开发速度,例如,这对于启动至关重要。 您只能在最慢的地方使用OSIV并优化数据库查询。 例如,当每个实体从相关集合中提取几个查询时,查询实体集合遭受的问题最大为n + 1。
因此,如果您想做正确的事,则需要在没有OSIV的情况下进行开发。 但是,如果开发速度和代码简单性很重要,则可以使用此模式,这会降低性能。
最主要的是,几年后,这种性能问题不会变成巨大的技术债务。 当开发人员不怀疑这些债务在他们身上积累时,这是双重危险,因为Spring Boot默默地为他们选择了Open Session In View的概念。 因此,作为上述请求的结果,决定在日志中显示有关所使用模式的警告是非常好的,我在本文开头引用了该警告。
我希望警告和这篇有关他的文章将帮助开发人员做出更明智的决定-是否在Spring Boot的应用程序中使用Open Session In View的概念。 我已经引用并讨论了支持和反对的主要论点,并且我建议阅读原始讨论。 这个问题表明,Spring / Spring Boot中的大量自动配置和默认设置对于不专心的开发人员可能是危险的。
您在Spring Boot应用程序中使用OSIV吗? 如果不是,表示层与数据库交互的体系结构如何组织? 为此使用了哪些技术和/或库?