【问题标题】:JPA EntityGraph making a Cartesian Product with subgraphJPA EntityGraph 使用子图制作笛卡尔积
【发布时间】:2021-06-09 18:22:24
【问题描述】:

我正在使用 Spring Data JPA 和实体图。 我有以下实体结构: Result 实体有一个SingleQuestionResponse 实体列表,SingleQuestionResponse 实体有一组Answer 实体(标记答案)。

public class Result {
...

 @OneToMany(cascade = CascadeType.PERSIST)
 @JoinColumn(name = "result_id", nullable = false)
 private List<SingleQuestionResponse> responses;

...

}
public class SingleQuestionResponse {
...

@ManyToMany(fetch = FetchType.LAZY)
@JoinTable(
        name = "singlequestionresponses_answers",
        joinColumns = @JoinColumn(name = "single_question_response_id"),
        inverseJoinColumns = @JoinColumn(name = "answer_id")
)
private Set<Answer> markedAnswers;

...
}

Answer 只有简单类型的字段。

现在,我希望能够在一个查询中获取结果、所有响应以及标记的答案。为此,我在 Result 类中注释了:

@NamedEntityGraph(name = "graph.Result.responsesWithQuestionsAndAnswersEager",
        attributeNodes = @NamedAttributeNode(value = "responses", subgraph = "responsesWithMarkedAnswersAndQuestion"),
        subgraphs = {
                @NamedSubgraph(name = "responsesWithMarkedAnswersAndQuestion", attributeNodes = {
                        @NamedAttributeNode("markedAnswers"),
                        @NamedAttributeNode("question")
                })
        }
)

一个使用例子是:

@EntityGraph("graph.Result.responsesWithQuestionsAndAnswersEager")
List<Result> findResultsByResultSetId(Long resultSetId);

我注意到,调用findResultsByResultSetId 方法(以及使用此实体图的其他方法)导致responsesSingleQuestionResponse 实体)乘以markedAnswers 的数量。我的意思是result.getResponses() 返回的SingleQuestionResponse 对象比它应该返回的对象多(每个标记的答案返回一个响应对象)。

我意识到这是由于 Hibernate 使用连接生成笛卡尔积,但我不知道如何解决它。

你能帮忙吗?谢谢

【问题讨论】:

    标签: sql hibernate join spring-data-jpa entitygraph


    【解决方案1】:

    您必须使用DISTINCT 运算符。使用 Spring Data JPA,这可以通过将方法命名为 findDistinctResultsByResultSetId

    来完成

    【讨论】:

    • 我认为这将区分根实体Result,而 OP 问题涉及“中间”关系,将SingleQuestionResponse 集合存储在List 中。有什么方法可以指示 Hibernate 去重复那个,而不用在 Set 中转收集?
    • Distinct 默认传递给 SQL,因此它通常也会对列表中的元素进行重复数据删除。
    • 是的。但是 hibernate 将传递一个 distinct 组合所有选定的列,因此每一行的结果都会与其他行不同。在 mysql cli 中测试复制/粘贴生成的查询。 “根区分”发生在内存中的另一个证据是,如果通过HINT_PASS_DISTINCT_THROUGH,它会生成完全相同的结果。目前,DB 生成的结果集将始终是所有三个连接表的笛卡尔积,而内存中的 Hibernate 只会对第一个表进行重复数据删除。
    • 如果多个集合被连接提取,那么是的,使用 distinct 将无济于事。在这种情况下,您将不得不在任何地方使用Set 或使用SELECTSUBSELECT 获取
    猜你喜欢
    • 1970-01-01
    • 2015-11-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-06-15
    相关资源
    最近更新 更多