这个 spring.jpa.open-in-view=true 属性在 Spring Boot 中是什么?
这个 spring.jpa.open-in-view=true 属性在 Spring Boot 中是什么?
我在Spring Boot JPA配置的文档中看到了 spring.jpa.open-in-view=true
属性。
- 如果没有提供该属性,
true
是否为默认值? - 这个属性真正做了什么?我没有找到任何好的解释;
- 它是否使您使用
SessionFactory
而不是EntityManagerFactory
?如果是,我该如何告诉它允许我使用EntityManagerFactory
?
谢谢!
OSIV反模式
开放会话试图(Open Session in View)强制将持久上下文保持打开状态,以便视图层可以触发代理初始化,而不是让业务层决定如何最好地获取视图层所需的所有关联。下图说明了该过程:
OpenSessionInViewFilter
调用底层SessionFactory
的openSession
方法并获取一个新的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
,底层数据库连接也会被释放。
乍一看,这可能看起来不是一件可怕的事情,但从数据库角度来看,一系列缺陷开始变得更加明显。
服务层开启和关闭数据库事务,但之后,并没有明确的事务。因此,UI渲染阶段发出的每一条额外语句都在自动提交模式下执行。自动提交会给数据库服务器带来压力,因为每个事务都会在结束时发出提交命令,这可能会触发将事务日志刷新到磁盘上。一种优化方法是将Connection
标记为只读,这将使数据库服务器避免写入事务日志。
由于语句既由服务层生成,也由UI渲染过程生成,因此不再有关注点的分离。编写断言正在生成的语句数量的集成测试需要通过所有层(web、服务、DAO),同时将应用程序部署在web容器上。即使使用内存数据库(例如HSQLDB)和轻量级web服务器(例如Jetty),这些集成测试执行起来也比分离层且后端集成测试使用数据库,而前端集成测试则完全模拟服务层更慢。
UI层仅限于导航关联,这可能会触发N+1查询问题。尽管Hibernate提供了批量获取关联的@BatchSize
和用于应对该场景的FetchMode.SUBSELECT
,但注释会影响默认的获取计划,因此它们将应用于每个业务用例。因此,数据访问层查询更加适合,因为它可以为当前用例的数据获取要求进行调整。
最后但并非最不重要的,数据库连接在UI渲染期间保持,这增加了连接租赁时间并限制了整体事务吞吐量,因为数据库连接池拥塞。连接被保持得越久,其他并发请求就越会等待从池中获取连接。
Spring Boot和OSIV
不幸的是,在Spring Boot中默认启用了OSIV(Open Session in View),从性能和可扩展性的角度来看,OSIV真的是一个不好的想法。
因此,请确保在application.properties
配置文件中有以下条目:
spring.jpa.open-in-view=false
这将禁用OSIV,以便您可以正确处理LazyInitializationException
。
从2.0版本开始,Spring Boot默认启用OSIV时会发出警告,因此您可以在它影响到生产系统之前发现此问题。