gpt4 book ai didi

java - 使用 JPA 和 Hibernate 加载没有 N+1 笛卡尔积的递归对象图

转载 作者:太空狗 更新时间:2023-10-29 22:47:31 26 4
gpt4 key购买 nike

在将项目从 Ibatis 转换为 JPA 2.1 时,我遇到了一个问题,我必须为一组对象加载完整的对象图,而出于性能原因,没有进行 N+1 选择或使用笛卡尔积。

用户查询将产生一个列表 ,我需要确保当我返回任务时,它们已经填充了所有属性,包括 parentchildren依赖项属性。先解释一下涉及到的两个实体对象。

任务是层次结构的一部分。它可以有父任务,也可以有子任务。任务可以依赖于其他任务,由“依赖项”属性表示。一个任务可以有很多属性,由 properties 属性表示。

已尽可能简化示例对象并删除样板代码。

@Entity
public class Task {
@Id
private Long id;

@ManyToOne(fetch = LAZY)
private Task parent;

@ManyToOne(fetch = LAZY)
private Task root;

@OneToMany(mappedBy = "task")
private List<TaskProperty> properties;

@ManyToMany
@JoinTable(name = "task_dependency", inverseJoinColumns = { @JoinColumn(name = "depends_on")})
private List<Task> dependencies;

@OneToMany(mappedBy = "parent")
private List<Task> children;
}

@Entity
public class TaskPropertyValue {
@Id
private Long id;

@ManyToOne(fetch = LAZY)
private Task task;

private String name;
private String value;
}

给定任务的任务层次结构可以无限深,因此为了更容易获得整个图,任务将通过“根”属性有一个指向其根任务的指针。

在 Ibatis 中,我只是获取了根 ID 的不同列表的所有任务,然后使用“task_id IN ()”查询对所有属性和依赖项进行了临时查询。当我有了这些时,我使用 Java 代码将属性、子项和依赖项添加到所有模型对象,以便完成图形。对于任何大小的任务列表,我只会执行 3 个 SQL 查询,并且我正在尝试对 JPA 执行相同的操作。由于“parent”属性指示添加子项的位置,因此我什至不必查询它们。

我尝试过不同的方法,包括:

让延迟加载完成它的工作

  • 表演自杀,无需细说:)

JOIN FETCH 子级、JOIN FETCH 依赖项、JOIN FETCH 属性

  • 这是有问题的,因为生成的笛卡尔积非常大,而且我的 JPA 实现 (Hibernate) 不支持 List,仅在获取多个包时支持 Set。一项任务可能具有大量属性,使得笛卡尔积无效。

与我在 ibatis 中使用的方式相同的 Ad-hoc 查询

  • 我无法将子项、依赖项和属性添加到 Task 对象的惰性初始化集合中,因为 Hibernate 随后会尝试将它们添加为新对象。

一个可能的解决方案是创建不受 JPA 管理的新 Task 对象,并使用它们将我的层次结构缝合在一起,我想我可以接受它,但感觉不是很“JPA”,然后我无法将 JPA 用于其擅长的领域 - 自动跟踪和保存对我的对象的更改。

如有任何提示,我们将不胜感激。如有必要,我愿意使用供应商特定扩展。我在 Wildfly 8.1.0.Final(Java EE7 完整配置文件)和 Hibernate 4.3.5.Final 中运行。

最佳答案

可用选项

有一些策略可以实现您的目标:

  • 子选择提取会加载所有带有附加子选择的惰性实体,这是您第一次需要该给定类型的惰性关联时。乍一看这听起来很吸引人,但它会使您的应用容易受到要获取的额外子选择实体数量的影响,并可能传播到其他服务方法。

  • 批量获取更容易控制,因为您可以强制在一批中加载实体的数量,并且可能不会影响太多其他用例。

  • 如果您的数据库支持,则使用递归公用表表达式。

未雨绸缪

最后,这完全取决于您计划对所选行执行的操作。如果只是将它们显示到 View 中,那么原生查询就足够了。

如果您需要在多个请求中保留实体(首先是 View 部分,第二个是更新部分),那么实体是更好的方法。

从您的回复中,我看到您需要发出 EntityManager.merge() 并且可能依赖于级联来传播子级的状态转换(添加/删除)。

由于我们讨论的是 3 个 JPA 查询,只要您没有获得笛卡尔积,那么您应该可以使用 JPA。

结论

您应该争取最少的查询量,但这并不意味着您总是必须有一个且只有一个查询。两个或三个查询根本不是问题。

只要您控制查询数量并且不陷入 N+1 查询问题],您也可以接受多个查询。无论如何,用一个笛卡尔积(2 个一对多提取)换取一个连接和一个额外的选择是一笔不错的交易。

最后,您应该始终检查 EXPLAIN ANALYZE 查询计划并加强/重新考虑您的策略。

关于java - 使用 JPA 和 Hibernate 加载没有 N+1 笛卡尔积的递归对象图,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/24341533/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com