OneToMany:获取集合的大小
OneToMany: get size of collection
我正在使用 Spring Data JPA(内置 Hibernate)。我有两个实体测验和问题。它们之间的一对多关系。测验有问题列表。我如何获得有关测验和问题列表大小的信息?如果我这样写:
Quiz quiz = quizRepository.findOne(1);
int questionCount = quiz.getQuestions().size();
Hibernate 生成两个查询:
- Select 关于测验的信息
- Select 所有问题
但我只需要测验信息和问题的大小。
没有秒select怎么办?
您似乎需要将 FetchType=EAGER 添加到测验中的问题集合 class。
在这种情况下,所有问题(不仅是大小,还有所有内容)都将通过测验在一次查询中获取。
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",
在这种情况下,将从该查询中仅针对测验急切地获取问题。
您需要的是映射中带有 fetch
属性 的 JOIN
值,它将仅生成一个 select 查询并允许您获取大小你的 collection:
@OneToMany(fetch = FetchType.JOIN, mappedBy = "quiz")
public Set<Question> getQuestions() {
...
}
你可以在 Hibernate – fetching strategies examples 找到更多相关信息,它解释了所有的抓取策略、它们生成的查询以及它们之间的区别,你会看到:
With the fetch=”join” or @Fetch(FetchMode.JOIN)
Hibernate generated only one select statement, it retrieves all its related collections when the Stock
is initialized.
从您的问题中不清楚您是只想避免第二个 SQL 查询,还是想完全避免将问题加载到内存中。
如其他地方所述,您可以通过在关系本身或通过加载测验的 criteria/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,它具有相同的信息。然后,您可以将其映射为普通实体,并将其 link 作为一对一关系或 @SecondaryTable
.
映射到测验
如果您愿意使用特定于 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
两次,一次用于实际收集,另一次仅用于计数。
我正在使用 Spring Data JPA(内置 Hibernate)。我有两个实体测验和问题。它们之间的一对多关系。测验有问题列表。我如何获得有关测验和问题列表大小的信息?如果我这样写:
Quiz quiz = quizRepository.findOne(1);
int questionCount = quiz.getQuestions().size();
Hibernate 生成两个查询:
- Select 关于测验的信息
- Select 所有问题
但我只需要测验信息和问题的大小。
没有秒select怎么办?
您似乎需要将 FetchType=EAGER 添加到测验中的问题集合 class。
在这种情况下,所有问题(不仅是大小,还有所有内容)都将通过测验在一次查询中获取。
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",
在这种情况下,将从该查询中仅针对测验急切地获取问题。
您需要的是映射中带有 fetch
属性 的 JOIN
值,它将仅生成一个 select 查询并允许您获取大小你的 collection:
@OneToMany(fetch = FetchType.JOIN, mappedBy = "quiz")
public Set<Question> getQuestions() {
...
}
你可以在 Hibernate – fetching strategies examples 找到更多相关信息,它解释了所有的抓取策略、它们生成的查询以及它们之间的区别,你会看到:
With the
fetch=”join” or @Fetch(FetchMode.JOIN)
Hibernate generated only one select statement, it retrieves all its related collections when theStock
is initialized.
从您的问题中不清楚您是只想避免第二个 SQL 查询,还是想完全避免将问题加载到内存中。
如其他地方所述,您可以通过在关系本身或通过加载测验的 criteria/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,它具有相同的信息。然后,您可以将其映射为普通实体,并将其 link 作为一对一关系或 @SecondaryTable
.
如果您愿意使用特定于 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
两次,一次用于实际收集,另一次仅用于计数。