Spring Boot中的Spring .jpa.open-in-view=true属性是什么?

我在Spring Boot文档中看到了JPA配置的spring.jpa.open-in-view=true属性。

  • 如果根本没有提供该属性,true是该属性的默认值吗?
  • 这到底是做什么的?我找不到任何好的解释;
  • 它是否让你使用SessionFactory而不是EntityManagerFactory?如果是,我如何告诉它允许我使用EntityManagerFactory代替?

谢谢!

209680 次浏览

这个属性将注册一个OpenEntityManagerInViewInterceptor,它将注册一个EntityManager到当前线程,所以在web请求完成之前,你将拥有相同的EntityManager。它与Hibernate SessionFactory等无关。

OSIV反模式

.

OSIV(视图中的开放会话)不是让业务层决定如何最好地获取视图层所需的所有关联,而是强制持久化上下文保持打开状态,以便视图层可以触发代理初始化,如下图所示。

OSIV Anti-Pattern

  • 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, UI依次导航惰性关联并触发它们的初始化。
  • OpenSessionInViewFilter可以关闭Session,底层数据库连接也会被释放。

乍一看,这可能不是一件可怕的事情,但是,一旦从数据库的角度来看,一系列缺陷就开始变得更加明显。

服务层打开和关闭数据库事务,但之后,没有显式的事务进行。因此,从UI呈现阶段发出的每个附加语句都以自动提交模式执行。自动提交会给数据库服务器带来压力,因为每个事务在结束时都会提交,这会触发事务日志刷新到磁盘。一种优化方法是将Connection标记为只读,这将允许数据库服务器避免写入事务日志。

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

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

最后但并非最不重要的一点是,数据库连接在整个UI呈现阶段都是保持的,这增加了连接租用时间,并由于数据库连接池拥塞而限制了总体事务吞吐量。连接保持的越多,其他并发请求等待从池中获得连接的次数就越多。

Spring - Boot -and- OSIV

不幸的是,从性能和可伸缩性的角度来看,在Spring Boot中,默认情况下启用了OSIV(视图中的开放会话)和OSIV确实是一个坏主意。

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

spring.jpa.open-in-view=false

这将禁用OSIV,以便您可以正确处理LazyInitializationException

从2.0版本开始,Spring Boot问题OSIV时的警告在默认情况下是启用的,所以你可以在它影响生产系统之前发现这个问题。

可能是晚了,但我试图挖掘更多关于关闭和打开的含义,我发现这篇文章很有用spring-open-session-in-view

希望这能帮助到一些人…