java Hibernate 抛出的 MultipleBagFetchException
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/13334831/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
MultipleBagFetchException thrown by Hibernate
提问by LuckyLuke
I want to have an option in my repository layer to eager load entites, so I tried adding a method that should eager load a question entity with all the relationships, but it throws MultipleBagFetchException. How can I fix this? I am using Hibernate 4.16.
我想在我的存储库层中有一个选项来预先加载实体,所以我尝试添加一个方法,该方法应该预先加载具有所有关系的问题实体,但它抛出 MultipleBagFetchException。我怎样才能解决这个问题?我正在使用休眠 4.16。
@NamedQuery(name = Question.FIND_BY_ID_EAGER, query = "SELECT q FROM Question q LEFT JOIN FETCH q.answers LEFT JOIN FETCH q.categories LEFT JOIN FETCH q.feedback LEFT JOIN FETCH q.participant WHERE q.id = :id"),
How do I get a question object which is initially lazy loaded, to be eager loaded with all relations?
我如何获得一个最初延迟加载的问题对象,以便立即加载所有关系?
回答by Arjan Tijms
This is a rather nasty problem in Hibernate and actually ORM in general.
这在 Hibernate 和一般的 ORM 中是一个相当棘手的问题。
What happens is that the many (fetch) joins cause a rather large cartesian product to be created. I.e for ever other join new columns and new rows appear in the result, leading to a (fairly) large 'square' result.
发生的情况是许多 (fetch) 连接会导致创建一个相当大的笛卡尔积。即永远其他连接新列和新行出现在结果中,导致(相当)大的“正方形”结果。
Hibernate needs to distill a graph from this table, but it's not smart enough to match the right columns to the right entities.
Hibernate 需要从这个表中提取一个图形,但它不够聪明,无法将正确的列与正确的实体相匹配。
E.g.
例如
Suppose we have the result
假设我们有结果
A B C
A B D
Which needs to become:
需要变成:
A
|
B
/\
C D
Hibernate could deduct from the primary keys and some encoding magic, what the graph must be, but in practice it needs explicit help to pull this off.
Hibernate 可以从主键和一些编码魔法中推断出图必须是什么,但实际上它需要明确的帮助来实现这一点。
One way to do this is by specifying the Hibernate specific @IndexColumn
or the JPA standard @OrderColumn
on the relations.
一种方法是通过在关系上指定 Hibernate 特定@IndexColumn
或 JPA 标准@OrderColumn
。
E.g.
例如
@Entity
public class Question {
@ManyToMany
@JoinTable(
name = "question_to_answer",
joinColumns = @JoinColumn(name = "question_id"),
inverseJoinColumns = @JoinColumn(name = "answer_id")
)
@IndexColumn(name = "answer_order")
private List<Answer> answers;
// ...
}
In this example I'm using a join table, with an extra column answer_order
. Via this column, which has a unique sequential number per Question/Answer relation, Hibernate can distinguish the entries in the result table and create the required Object graph.
在这个例子中,我使用了一个带有额外列的连接表answer_order
。通过这个列,每个问题/答案关系都有一个唯一的序列号,Hibernate 可以区分结果表中的条目并创建所需的对象图。
One note btw, if it concerns more than a few entities, using so many eager joins can potentially lead to a much larger result set than you might think based on the number of entities involved.
顺便说一句,如果它涉及多个实体,那么使用如此多的急切连接可能会导致比您根据所涉及的实体数量想象的要大得多的结果集。
Further reading:
进一步阅读:
回答by Vlad Mihalcea
This is a recurring question I've seen both on StackOverflow or the Hibernate forum, so I decided to turn the answer into an article.
这是一个反复出现的问题,我在 StackOverflow 或 Hibernate 论坛上都看到过,所以我决定把答案变成一篇文章。
Hibernate doesn't allow fetching more than one bag because that would generate a Cartesian product, and for unordered Lists, which are called baggsin Hibernate terminology, this will cause duplicate entries even if the underlying collection does not have those duplicated rows. So, Hibernate simply prevents this situation when the JPQL query is compiled.
Hibernate 不允许获取多个包,因为这会生成笛卡尔积,对于无序列表,在 Hibernate 术语中称为baggs,即使底层集合没有那些重复的行,这也会导致重复条目。因此,Hibernate 只是在编译 JPQL 查询时阻止了这种情况。
Now, you will find lots of answers, blog posts, videos, or other resources telling you to use a Set
instead of a List
for your collections.
现在,您会发现很多答案、博客文章、视频或其他资源都告诉您对您的收藏使用 aSet
而不是 a List
。
That's terrible advice. Don't do that!
这是可怕的建议。不要那样做!
Using Sets
instead of Lists
will make the MultipleBagFetchException
go away, but the Cartesian Product will still be there.
使用Sets
而不是Lists
会MultipleBagFetchException
消失,但笛卡尔积仍然存在。
The right fix
正确的修复
Instead of using multiple JOIN FETCH
in a single JPQL or Criteria API query:
而不是JOIN FETCH
在单个 JPQL 或 Criteria API 查询中使用多个:
List<Post> posts = entityManager
.createQuery(
"select p " +
"from Post p " +
"left join fetch p.comments " +
"left join fetch p.tags " +
"where p.id between :minId and :maxId", Post.class)
.setParameter("minId", 1L)
.setParameter("maxId", 50L)
.getResultList();
You can do the following trick:
您可以执行以下技巧:
List<Post> posts = entityManager
.createQuery(
"select distinct p " +
"from Post p " +
"left join fetch p.comments " +
"where p.id between :minId and :maxId ", Post.class)
.setParameter("minId", 1L)
.setParameter("maxId", 50L)
.setHint(QueryHints.PASS_DISTINCT_THROUGH, false)
.getResultList();
posts = entityManager
.createQuery(
"select distinct p " +
"from Post p " +
"left join fetch p.tags t " +
"where p in :posts ", Post.class)
.setParameter("posts", posts)
.setHint(QueryHints.PASS_DISTINCT_THROUGH, false)
.getResultList();
As long as you fetch at most one collection using JOIN FETCH
, you will be fine. By using multiple queries, you will avoid the Cartesian Product since any other collection but the first one is fetched using a secondary query.
只要您使用 最多获取一个集合JOIN FETCH
,就可以了。通过使用多个查询,您将避免笛卡尔积,因为任何其他集合都是使用辅助查询获取的,但第一个集合除外。