以及您使用哪种替代策略来避免LazyLoadExceptions?
我确实了解到公开会议存在以下问题:
- 在不同的jvm中运行的分层应用程序
- 事务仅在最后提交,并且很可能您希望在结果之前提交。
但是,如果您知道您的应用程序在单个vm上运行,为什么不使用视图中开放会话策略来减轻痛苦呢?
以及您使用哪种替代策略来避免LazyLoadExceptions?
我确实了解到公开会议存在以下问题:
但是,如果您知道您的应用程序在单个vm上运行,为什么不使用视图中开放会话策略来减轻痛苦呢?
Answers:
因为从性能和理解的角度来看,在视图层中发送可能未初始化的代理(尤其是集合)并从那里触发休眠加载可能会造成麻烦。
了解:
使用OSIV会污染与数据访问层有关的视图层。
视图层没有准备好处理HibernateException
延迟加载时可能发生的情况,但大概是数据访问层。
性能:
OSIV倾向于在地毯下拖拉适当的实体负载-您往往不会注意到自己的集合或实体是延迟初始化的(也许是N + 1)。更多便利,更少控制。
更新:有关此主题的更多讨论,请参见OpenSessionInView反模式。作者列出了三个要点:
- 每个惰性初始化都会为您提供一个查询,这意味着每个实体将需要N + 1个查询,其中N是惰性关联的数量。如果屏幕上显示表格数据,则读取Hibernate日志是一个很大的提示,表明您不应该这样做
- 这完全破坏了分层体系结构,因为您在演示层中不满意DB。这是一个概念上的弊端,所以我可以接受,但是有一个必然结果
- 最后但并非最不重要的一点是,如果在获取会话时发生异常,则该异常将在页面的编写过程中发生:您无法向用户展示干净的错误页面,唯一可以做的就是在正文中写入错误消息
有关详细说明,您可以阅读我的“在视图中使用反模式打开会话”一文。否则,这是为什么不应该在View中使用Open Session的摘要。
在视图中打开会话采用了一种错误的方法来获取数据。与其让业务层决定如何最好地获取View层所需的所有关联,不如让持久性上下文保持打开状态,以便View层可以触发Proxy初始化。
OpenSessionInViewFilter
调用openSession
底层的方法SessionFactory
,并获得新的Session
。Session
被绑定到TransactionSynchronizationManager
。OpenSessionInViewFilter
调用doFilter
的的javax.servlet.FilterChain
对象引用和所述请求被进一步处理DispatcherServlet
被调用,并将HTTP请求路由到底层PostController
。PostController
呼叫PostService
拿到名单Post
的实体。PostService
打开一个新的事务,而HibernateTransactionManager
重用相同Session
,是由打开的OpenSessionInViewFilter
。PostDAO
获取Post
实体列表。PostService
承诺的潜在交易,但Session
不是封闭的,因为它是从外部打开。DispatcherServlet
开始渲染的UI,这反过来,导航懒惰协会,并触发其初始化。OpenSessionInViewFilter
可以关闭Session
,和底层数据库连接被释放为好。乍一看,这似乎并不可怕,但是,一旦从数据库角度来看,一系列缺陷将变得更加明显。
服务层打开和关闭数据库事务,但是此后,没有任何显式事务在进行。因此,从UI渲染阶段发出的所有其他语句都在自动提交模式下执行。自动提交给数据库服务器带来了压力,因为每个语句都必须将事务日志刷新到磁盘,因此在数据库侧会导致大量I / O通信。一种优化是将标记Connection
为只读,这将允许数据库服务器避免写入事务日志。
由于服务层和UI呈现过程都生成了语句,因此不再存在关注点分离。编写断言所生成语句数量的集成测试需要遍历所有层(Web,服务,DAO),同时将应用程序部署在Web容器上。即使在使用内存数据库(例如HSQLDB)和轻量级Web服务器(例如Jetty)时,这些集成测试的执行速度也要比分离层和后端集成测试使用数据库的速度慢。前端集成测试完全模拟了服务层。
UI层仅限于导航关联,这又会触发N + 1个查询问题。尽管Hibernate提供@BatchSize
了批量获取关联的功能,并且FetchMode.SUBSELECT
为了应对这种情况,但注释会影响默认的获取计划,因此它们会应用于每个业务用例。因此,数据访问层查询非常适合,因为它可以针对当前用例数据获取要求进行定制。
最后但并非最不重要的一点是,数据库连接可以在整个UI呈现阶段(取决于您的连接释放模式)保持,这会增加连接租用时间并由于数据库连接池上的拥塞而限制总体事务吞吐量。保持的连接越多,从池中获取连接的其他并发请求将等待的越多。
因此,要么保持连接时间太长,要么为单个HTTP请求获取/释放多个连接,从而对基础连接池造成压力,并限制了可伸缩性。
不幸的是,Spring Boot默认情况下启用了View中的Open Session。
因此,请确保在application.properties
配置文件中具有以下条目:
spring.jpa.open-in-view=false
这将禁用OSIV,这样就可以处理LazyInitializationException
的正确方法。
事务可以在服务层中提交-事务与OSIV不相关。这是Session
该保持打开状态,而不是一个交易-运行。
如果您的应用程序层分布在多台计算机上,那么您几乎不能使用OSIV-您必须初始化所需的所有内容,然后才能通过网络发送对象。
OSIV是一种利用延迟加载的性能优势的不错且透明的方法(即,您的代码都不知道它发生了)
如果您使用的是诸如Spring之类的Inversion of Control(IoC)容器,则可能需要阅读bean scoping。本质上,我是在告诉Spring给我一个Hibernate Session
对象,该对象的生命周期跨越整个请求(即,它是在HTTP请求的开始和结束时创建和销毁的)。我不必担心LazyLoadException
s或关闭会话,因为IoC容器可以为我管理。
如前所述,您将不得不考虑N + 1 SELECT性能问题。之后,您始终可以将Hibernate实体配置为在性能存在问题的地方进行急切的联接加载。
Bean作用域解决方案不是特定于Spring的。我知道PicoContainer提供相同的功能,并且我确定其他成熟的IoC容器也可以提供类似的功能。
我刚刚发布了一些有关何时在博客中使用公开会话的指导原则的文章。如果您有兴趣,请查看。
http://heapdump.wordpress.com/2010/04/04/should-i-use-open-session-in-view/
这不会有太大帮助,但是您可以在这里查看我的主题:* 使用OpenSessionInView的Hibernate Cache1 OutOfMemory
由于OpenSessionInView和加载的很多实体,我有一些OutOfMemory问题,因为它们保留在Hibernate缓存级别1中,并且没有进行垃圾回收(我加载了很多实体,每页包含500个项目,但是所有实体都保留在缓存中)