当前位置: 首页 > 面试题库 >

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

陆建木
2023-03-14
问题内容

将项目从Ibatis转换为JPA 2.1时,我面临一个问题,即我必须为一组对象加载完整的对象图,而不会由于性能原因而选择N + 1个选择或使用笛卡尔积。

用户查询将产生List ,并且我需要确保在返回任务时,它们具有填充的所有属性,包括 parentchildren
dependenciesproperties 。首先让我解释所涉及的两个实体对象。

任务是层次结构的一部分。它可以具有父任务,也可以具有子任务。一个任务可以依赖于其他任务,由’dependencies’属性表示。任务可以具有许多属性,这些
属性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;
}

给定任务的Task层次结构可以无限深,因此,为了更轻松地获取整个图形,任务将通过’root’属性具有指向其根任务的指针。

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

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

让延迟加载完成它的工作

  • 表现自杀,无需赘述:)

JOIN FETCH子代,JOIN FETCH依赖项,JOIN FETCH属性

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

临时查询与我在ibatis中所做的相同

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

一种可能的解决方案是创建不受JPA管理的新Task对象,并使用这些对象将我的层次结构缝合在一起,我想我可以接受,但是感觉不太“
JPA”,然后我无法使用JPA的优点是-自动跟踪和持久保存对我的对象的更改。

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


问题答案:

可用选项

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

  • 子选择获取将在第一次需要该给定类型的惰性关联时,为所有惰性实体加载附加的子选择。听起来一开始很吸引人,但它会使您的应用对要提取的其他子选择实体的数量脆弱,并且可能传播到其他服务方法。

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

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

未雨绸缪

最后,所有这些都与您计划对所选行的处理有关。如果只是将它们显示在视图中,那么原生查询就足够了。

如果您需要跨多个请求保留实体(第一个是视图部分,第二个是更新部分),则最好使用实体。

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

既然我们在谈论3个JPA查询,并且只要您没有得到笛卡尔积,就可以使用JPA。

结论

您应该争取最少的查询量,但这并不意味着您将永远只需要拥有一个查询。两个或三个查询根本不是问题。

只要您控制查询数量并且不涉及N + 1查询问题,您也可以使用多个查询。无论如何,将一个笛卡尔积(2个一对多取回)交易为一个连接和一个额外的选择。

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



 类似资料:
  • 的结果将是二维数组: 我试图做的是使用流在Java中编写这个笛卡尔乘积函数。 到目前为止,我有以下Java版本: 我对问题的猜测是: 我需要在某个地方使用收集器(可能在之后) 标识的数据类型错误

  • 问题内容: 我有两个pandas数据框: 获得其笛卡尔积的最佳实践是什么(当然不用像我这样明确地编写它)? 问题答案: 如果每行都有一个重复的键,则可以使用merge生成笛卡尔乘积(就像在SQL中一样)。 输出:

  • 问题内容: 在Tensorflow中有什么简单的方法可以像itertools.product一样做笛卡尔积吗?我想获得两个张量(和)的元素组合,在Python中可以通过itertools作为。我正在Tensorflow中寻找替代方案。 问题答案: 我将在此假定和均为一维张量。 为了得到两者的笛卡尔积,我会用的组合和: 您使用LEN(一) LEN(B) 2张量,其中的元件的每个组合结束并且在最后一维

  • 问题内容: 我对SQL还是很陌生,并且正在为查询而苦苦挣扎(使用Access,FWIW)。我已经搜索并搜索了StackOverflow,但没有看到这种确切的情况。(这也可能是因为我不知道正确的搜索词。) 我有两个非常简单的表,其中包含相似的数据。 我想要的是在两个表以及该人所在的每个表的网络中找到匹配的每个人/州组合: 此人在每个表中可能位于多个网络中。我想查看此人所属的每个网络(从两个表中)。

  • 问题内容: 我试图找到两个不同集合的笛卡尔积。我在网上找不到有关列表或字典的笛卡尔积的任何信息。 功率设置也很混乱。 我使用的书中都没有这两个。 你们中的一位可以指出我正确的方向。 问题答案: 对于笛卡尔积,请检出。 对于幂,该文档也给我们的食谱: 例如:

  • 问题内容: 我正在尝试编写一些代码来测试一堆输入参数的笛卡尔积。 我看过了,但是它的功能并不是我想要的。有没有一种简单明了的方法来获取一个字典,每个字典中包含任意数量的键 和 每个值中任意数量的元素,然后生成具有下一个排列的字典? 输入: 输出示例: 问题答案: 好的,感谢@dfan告诉我我在错误的位置查看。我现在知道了: 编辑 :经过多年的Python经验,我认为一个更好的解决方案是接受输入,而