【问题标题】:OneToMany: get size of collectionOneToMany:获取集合的大小
【发布时间】:2015-09-08 19:21:00
【问题描述】:

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

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

Hibernate 生成两个查询:

  1. 选择有关测验的信息
  2. 选择所有问题

但我只需要测验信息和问题的大小。

如果没有第二次选择,我该怎么做?

【问题讨论】:

    标签: hibernate jpa spring-data


    【解决方案1】:

    您似乎需要在 Quiz 类的 Qustions 集合中添加 FetchType=EAGER。

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

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

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

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

    在这种情况下,将只为此查询中的测验急切地提取问题。

    【讨论】:

      【解决方案2】:

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

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

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

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

      【讨论】:

        【解决方案3】:

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

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

        对于第二种情况,您有多种选择。 Hibernate 特定的非便携式解决方案是利用 Hibernate 的 @Formula 注释。

        How to map calculated properties with JPA and Hibernate

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

        或者使用 Hibernate @LazyCollection(LazyCollectionOption.EXTRA) 属性,它允许您在不加载所有记录的情况下调用 size()

        这两种方法都会为您提供问题的数量,而无需将整个集合加载到内存中。

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

        【讨论】:

        • 谢谢。我以前不知道@Formula。但我已经找到了很好的解决方案。 @LazyCollection(LazyCollectionOption.EXTRA) 私人列表 问题; Hibernate 不会加载所有相关的问题,只会加载大小。
        • 是的,你是对的。这可能是最好的解决方案:忘记了 Hibernate 选项!
        【解决方案4】:

        如果您愿意使用特定于 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 两次,一次用于实际收集,另一次仅用于计数。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2019-07-06
          • 1970-01-01
          • 2012-11-12
          • 1970-01-01
          • 1970-01-01
          • 2013-03-27
          • 1970-01-01
          • 2021-04-08
          相关资源
          最近更新 更多