HeD*_*ges 104 java hibernate jpa lazy-loading open-session-in-view
您使用什么样的替代策略来避免LazyLoadExceptions?
我确实理解在视图中打开会话有以下问题:
但是,如果您知道您的应用程序在单个虚拟机上运行,为什么不通过在视图策略中使用开放会话来减轻您的痛苦?
Rob*_*anu 45
因为在视图层中发送可能未初始化的代理(尤其是集合)并从中触发休眠加载可能会从性能和理解的角度来解决问题.
理解:
使用OSIV"污染"视图层,其中涉及与数据访问层相关的问题.
视图层不准备处理HibernateException延迟加载时可能发生的情况,但可能是数据访问层.
表现:
OSIV倾向于在地毯下拖拽适当的实体加载 - 您往往不会注意到您的集合或实体被懒惰地初始化(可能是N + 1).更方便,更少控制.
更新:有关此主题的更多讨论,请参阅OpenSessionInView反模式.作者列出了三个要点:
- 每个延迟初始化将获得一个查询意味着每个实体将需要N + 1个查询,其中N是延迟关联的数量.如果您的屏幕显示表格数据,那么阅读Hibernate的日志是一个很大的暗示,您没有按照自己的意愿去做
- 这完全打败了分层架构,因为你在表达层中用DB玷污你的指甲.这是一个概念上的概念,所以我可以忍受它,但有一个必然结果
- 最后但并非最不重要的是,如果在获取会话时发生异常,则会在页面写入期间发生:您无法向用户显示干净的错误页面,您唯一能做的就是在正文中写入错误消息
Vla*_*cea 39
有关更长的说明,您可以阅读我在视图反模式中的开放会话文章.否则,这里是您不应该在视图中使用Open Session的原因摘要.
Open Session In View采用糟糕的方法获取数据.它不是让业务层决定如何最好地获取View层所需的所有关联,而是强制Persistence Context保持打开状态,以便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中默认启用Open Session in View.
因此,请确保在application.properties配置文件中包含以下条目:
spring.jpa.open-in-view=false
Run Code Online (Sandbox Code Playgroud)
这将禁用OSIV,以便您可以正确的方式处理LazyInitializationException.
Boz*_*zho 24
事务可以在服务层中提交 - 事务与OSIV无关.这Session是保持开放,而不是交易 - 运行.
如果您的应用程序层分布在多台机器上,那么您几乎无法使用OSIV - 您必须在通过网络发送对象之前初始化您需要的所有内容.
OSIV是一个很好的透明(即 - 你的代码都没有意识到它发生了)的方式来利用延迟加载的性能优势
Geo*_*man 13
我不会说Open Session In View被认为是一种不好的做法; 什么给你这种印象?
Open-Session-In-View是一种处理Hibernate会话的简单方法.因为它很简单,有时简单化.如果您需要对事务进行细粒度控制,例如在请求中包含多个事务,则Open-Session-In-View并不总是一种好方法.
正如其他人所指出的那样,对OSIV进行一些权衡 - 你更容易出现N + 1问题,因为你不太可能意识到你正在开展什么交易.同时,这意味着您无需更改服务图层以适应视图中的微小更改.
如果您使用的是Inversion of Control(IoC)容器(如Spring),您可能需要阅读bean范围.本质上,我告诉Spring给我一个Hibernate Session对象,其生命周期跨越整个请求(即,它在HTTP请求的开始和结束时被创建和销毁).LazyLoadException因为IoC容器为我管理,所以我不必担心s或关闭会话.
如上所述,您将不得不考虑N + 1 SELECT性能问题.您可以随后配置您的Hibernate实体,以便在性能有问题的地方进行热切加入.
bean范围解决方案不是特定于Spring的.我知道PicoContainer提供相同的功能,我相信其他成熟的IoC容器提供类似的功能.