java - JPA Hibernate 延迟加载集合

标签 java spring hibernate jpa

我知道这已经被讨论了一遍又一遍(我自己在这个问题上有几个实现),也许这就是我问的部分原因:没有明确的答案(经过几个小时的搜索),我觉得这是第 22 条军规。

我的设置是 Spring Boot 1.3.6,它与 Hibernate 4.3.11“联姻”。我正在使用 Spring Data JPA。

我有一个实体,其中包含多个子实体的集合,@OneToMany@ManyToMany。这就是模型——它不值得我爱或恨。

然后我有一个普通的 CRUD UI,其中包含主实体的表和编辑器,用户可以在编辑器中为子实体选择选项并保存主实体。

UI 是用 Vaadin 完成的,并且涉及到 Web Sockets。所以 - 不是正常的 Http 交换。

使用正常设置,我从 Hibernate 得到“正常”LazyInitializationException。因为,一旦在表屏幕中加载实体, session 就会关闭,因此,当在编辑器屏幕中调用集合时,我要么完全重新加载实体,这会消除使用 ORM 的所有魔力,要么我尝试其他方法:

  • 使集合变得急切(并将它们拖到任何地方,更不用说尽管 @Fetch(FetchMode=JOIN) 出现的 n+1 问题,这本不应该出现在第一名)
  • spring.jpa.properties.hibernate.enable_lazy_load_no_trans=true添加到我的启动配置中,与第一个选项基本相同,变成默认值。不用了,谢谢。
  • 在检索父实体后(即在同一 session 中)对集合使用 Hibernate.initialize,这类似于编程式 EAGER 检索 - 但它与 Hibernate 高度耦合,并且不会为我节省额外的查询(n+1)。
  • 在我的 JPQL 查询中使用“fetch join”检索数据 - 除非这可能导致结果集升级到数千行(然后在内存中聚合),更不用说 A) 它需要 JPQL 来进行其他琐碎的查询,在 Spring Data 中使用简单的接口(interface)名称实现,B)如果你想要分页,你还需要编写 countingQuery (没有 join fetch),因为否则你会得到一个漂亮、清晰且易于调试的结果org.hibernate.QueryException:查询指定连接获取,但获取的关联的所有者不存在于选择列表中。另外,如果您“忘记”删除 @OneToMany@ManyToMany 注释上的 fetchType 选项,您会遇到另一个令人头疼的异常: org.hibernate.loader.MultipleBagFetchException:无法同时获取多个包
  • 按照 open-in-view 模式(或者更确切地说是反模式)创建一个过滤器,该过滤器将为同一某物中的所有请求检索相同的 EntityManager - 而不是 Http Session,因为我们使用 Web Sockets(或者几乎任何其他东西,Hibernate 不应该是 Web ORM);不是 Vaadin Session,因为我可能选择拥有多个 servlet,或者分发整个应用程序;或停止使用 Vaadin;不是线程本地,因为我可能选择在不同的线程池中执行操作,而不是从线程本地继承:同样,这不是 Hibernate 关心的问题。

最后,切换到 EclipseLink,它具有更明智的方法,只需在需要时在新 session 中请求子集合,这似乎比 Spring Boot 通常提供的更麻烦(有几个关于使用仪器代理运行应用程序的必要性的讨论仍在进行中)。

那么,最终是否有一个无黑客的解决方案来解决这个问题呢?

最佳答案

使用 Hibernate 有两种主要策略。

  • 长期 session (保持 db-session 与 http-session 并行)
  • 短暂的 session (在 conn-close 后抛出 LazyInitializationException)

LazyInitializationException 提示我们您正在使用short-lived-session

要么使用长期 session 并使用 MVC View 中的实体。 或者将实体中的所有值重新映射到非实体的新包装器。

我建议保留 short-lived-session 策略并将所有值重新映射到 View 的 pojo。因为:数据库结构很少是前端所需的信息结构。例如:

您有两个实体:

  • 用户
    • 长用户 ID
    • 字符串名字
    • 字符串姓氏
  • 地址
    • 长地址Id
    • 弦街
    • 字符串 zipper
    • 弦城
    • 长用户 ID

但是在 View 中你有这个数据对象(DTO):

  • 用户类
    • 字符串名字
    • 字符串姓氏
    • 弦街
    • 字符串 zipper
    • 弦城

你看,bean 是不同的,在 conn-close 后你不会得到一个 LazyInitializationException

关于java - JPA Hibernate 延迟加载集合,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/38955418/

相关文章:

spring - 使用 Angular2 将 MultipartFile 作为请求参数发送到 REST 服务器

java - 为什么 Spring 提供自己的任务执行器?

Hibernate:无法将数据库状态与 session 同步

java - @Before 和 @After 排除

java - 为什么我的 java -version 与我在 mvn --version 中看到的不同

java - 在 Java 中模拟 DTO 的最佳方法是什么?

eclipse 中未使用的 java 局部变量

java - 添加 Java 异常断点在 Eclipse 中不显示任何匹配项

java - 使用 Spring Integration 使 ssl-context-support 有条件地使用 tcp-connection-factory

java - Hibernate 仅更新选定的列而不是所有修改的字段