OneToMany:获取收藏的大小

时间:2015-06-23 06:19:44

标签: hibernate jpa spring-data

我正在使用Spring Data JPA(内部的Hibernate)。我有两个实体测验和问题。 OneToMany他们之间的关系。测验有问题清单。如何获取有关测验和问题列表大小的信息?如果我这样写:

Quiz quiz = quizRepository.findOne(1);
int questionCount = quiz.getQuestions().size();

Hibernate生成两个查询:

  1. 选择有关测验的信息
  2. 选择所有问题
  3. 但我只需要测验信息和问题的大小。

    如果没有第二次选择,我怎么能这样做?

4 个答案:

答案 0 :(得分:5)

如果您愿意使用特定于Hibernate的注释,则可以执行以下操作:

class Quiz {
  @LazyCollection(LazyCollectionOption.EXTRA)
  @OneToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY, mappedBy = "quiz", orphanRemoval = true)
  private Set<Question> questions;
}

请注意使用@LazyCollection(LazyCollectionOption.EXTRA)

现在,如果你执行quizRepsitory.findOne(...).getQuestions().size(),Hibernate将触发查询SELECT COUNT(...) FROM question WHERE quiz_id=?。但是,如果你执行for(Question question : quizRepsitory.findOne(...).getQuestions()) { ... },Hibernate将触发另一个查询:SELECT * FROM question WHERE quiz_id=?。此外,如果已加载quiz.questions,则不会再次触发COUNT查询。保存您必须将Quiz映射到Question两次,一次用于实际收集,再次用于计数。

答案 1 :(得分:1)

从您的问题中不清楚您是否只想避免第二个SQL查询,或者是否要完全避免将问题加载到内存中。

如其他地方所述,您可以通过在关系本身或通过加载测验的条件/ JPQLquery指定获取模式来处理第一个场景。这将在一个SQL查询中加载所有内容,但您仍然需要加载问题的开销:在您的情况下加载问题可能不是问题,但对于大型数据集,如果您只需要计数,则开销可能相当大。

对于第二种情况,您有两种选择。第一个,Hibernate特定的非便携式,是利用Hibernate的@Formula注释。

Calculated property with JPA / Hibernate

class Quiz{
    @Formula("select count(*) from question where quiz_id = id")
    int numberOfQuestions;
}

将为您提供问题数量,而无需将整个集合加载到内存中。

第二种非Hibernate特定的便携式解决方案是创建一个视图,例如vw_quiz_summary_data,它具有相同的信息。然后,您可以将其映射为普通实体,并将其作为一对一关系或@SecondaryTable链接到测验。

答案 2 :(得分:0)

您需要的是JOIN值与映射中的fetch属性,它只会生成一个选择查询,并允许您获取集合的大小:

@OneToMany(fetch = FetchType.JOIN, mappedBy = "quiz")
public Set<Question> getQuestions() {
  ...
}

您可以在 Hibernate – fetching strategies examples 找到更多相关信息,它解释了所有获取策略,生成的查询以及它们之间的差异,您将看到:

  

fetch=”join” or @Fetch(FetchMode.JOIN) Hibernate只生成一个select语句时,它会在初始化Stock时检索其所有相关集合。

答案 3 :(得分:-1)

您似乎需要在Quiz类中将QetchType = EAGER添加到Qustions集合中。

在这种情况下,所有问题(不仅是大小,还包括所有内容)都将通过测验在一个查询中获取。

class Quiz {
...
@OneToMany(fetch = FetchType.EAGER, mappedBy = "quiz")
    public Set<Question> getStockDailyRecords() {
        return this.stockDailyRecords;
    }
 ...
}

如果您在所有情况下都不需要急切提取问题 - 您可以在查询中使用获取加入:

@Query(value = "SELECT q FROM Quiz q LEFT JOIN FETCH q.questions",

在这种情况下,只会针对此查询中的测验提出问题。