Java org.hibernate.loader.MultipleBagFetchException:无法同时获取多个包
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/24675340/
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
org.hibernate.loader.MultipleBagFetchException: cannot simultaneously fetch multiple bags
提问by xrcwrn
Following is my code Here I am using multiple lists to fetch data from database. On fetching data from hql query it is showing exception.
以下是我的代码这里我使用多个列表从数据库中获取数据。从 hql 查询中获取数据时显示异常。
Pojo Class
Pojo 类
public class BillDetails implements java.io.Serializable {
private Long billNo;
// other fields
@LazyCollection(LazyCollectionOption.FALSE)
private List<BillPaidDetails> billPaidDetailses = new ArrayList<BillPaidDetails>();
private Set productReplacements = new HashSet(0);
@LazyCollection(LazyCollectionOption.FALSE)
private List<BillProduct> billProductList = new ArrayList<BillProduct>();
//getter and setter
}
hmb.xml file
hmb.xml 文件
<class name="iland.hbm.BillDetails" table="bill_details" catalog="retail_shop">
<id name="billNo" type="java.lang.Long">
<column name="bill_no" />
<generator class="identity" />
</id>
<bag name="billProductList" table="bill_product" inverse="true" lazy="false" fetch="join">
<key>
<column name="bill_no" not-null="true" />
</key>
<one-to-many class="iland.hbm.BillProduct" />
</bag>
<bag name="billPaidDetailses" table="bill_paid_details" inverse="true" lazy="false" fetch="select">
<key>
<column name="bill_no" not-null="true" />
</key>
<one-to-many class="iland.hbm.BillPaidDetails" />
</bag>
<set name="productReplacements" table="product_replacement" inverse="true" lazy="false" fetch="join">
<key>
<column name="bill_no" not-null="true" />
</key>
<one-to-many class="iland.hbm.ProductReplacement" />
</set>
</class>
Hql query
后台查询
String hql = "select distinct bd,sum(bpds.amount) from BillDetails as bd "
+ "left join fetch bd.customerDetails as cd "
+ "left join fetch bd.billProductList as bpd "
+ "left join fetch bpd.product as pd "
+"left join fetch bd.billPaidDetailses as bpds "
+ "where bd.billNo=:id "
+ "and bd.client.id=:cid ";
I am trying following query to fetch data from database but this is showing
org.hibernate.loader.MultipleBagFetchException: cannot simultaneously fetch multiple bags
How to resolve this
我正在尝试按照查询从数据库中获取数据,但这显示了
org.hibernate.loader.MultipleBagFetchException: cannot simultaneously fetch multiple bags
如何解决此问题
采纳答案by Vlad Mihalcea
This is a very common question, so I decided to turn the answer into an article.
这是一个很常见的问题,所以我决定把答案变成一篇文章。
Hibernate doesn't allow fetching more than one bag because that would generate a Cartesian product.
Hibernate 不允许获取多个包,因为这会生成笛卡尔积。
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
而不是 aList
作为您的收藏。
That's terrible advice!
这是可怕的建议!
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
,就可以了。通过使用多个查询,您将避免使用笛卡尔积,因为任何其他集合都是使用辅助查询获取的,但第一个集合除外。
回答by Pracede
Your request fetch too many data and HIbernate cannot load them all. Reduce your request and/or configure your entities to retrieve just needed data
您的请求获取了太多数据,而 HIbernate 无法全部加载它们。减少您的请求和/或配置您的实体以检索刚需要的数据
回答by enlait
You can only join-fetch following one relation for an entity (either billPaidDetailses
or billProductList
).
您只能在实体的一个关系(billPaidDetailses
或billProductList
)之后加入提取。
Consider using lazy associations and loading collections when they are needed, OR using lazy associations and loading collections manually with Hibernate.initialize(..)
. At least that was the conclusion I came to when I had a similar issue.
考虑在需要时使用延迟关联和加载集合,或者使用延迟关联并手动加载集合Hibernate.initialize(..)
。至少这是我在遇到类似问题时得出的结论。
Either way it will take more than one query to the database.
无论哪种方式,都需要对数据库进行多次查询。
回答by L. Holanda
Changing to Set
is the best solution. However, if you cannot not replace the List
with Set
(like in my case, there was a heavy use of JSF tags specific to Lists
), and if you can use Hibernate proprietary annotations, you can specify @IndexColumn (name = "INDEX_COL")
. That solution worked better for me, changing to Set
would require tons of refactoring.
改成Set
是最好的解决办法。但是,如果您不能替换List
with Set
(就像在我的情况下,大量使用了特定于 的 JSF 标记Lists
),并且如果您可以使用 Hibernate 专有注释,则可以指定@IndexColumn (name = "INDEX_COL")
. 该解决方案对我来说效果更好,更改为Set
需要大量重构。
So, your code would be something like this:
所以,你的代码应该是这样的:
@IndexColumn (name = "INDEX_COL")
private List<BillPaidDetails> billPaidDetailses = new ArrayList<BillPaidDetails>();
@IndexColumn (name = "INDEX_COL")
private List<BillProduct> billProductList = new ArrayList<BillProduct>();
As Igor suggested in the comments, you could also create proxy methods to return the lists. I haven't tried that, but would be a good alternative if you cannot use Hibernate proprietary annotations.
正如 Igor 在评论中建议的那样,您还可以创建代理方法来返回列表。我还没有尝试过,但如果您不能使用 Hibernate 专有注释,那将是一个不错的选择。
回答by BERGUIGA Mohamed Amine
For me I had the same error and I solved by adding the annotation of hibernate @Fetch
对我来说,我遇到了同样的错误,我通过添加 hibernate @Fetch的注释解决了
@OneToMany(mappedBy="parent", fetch=FetchType.EAGER)
@Fetch(value = FetchMode.SUBSELECT)
private List<Child> childs;
回答by hakson
I used the new annotation @OrderColumn instead of @IndexColumn (deprecated see: https://docs.jboss.org/hibernate/orm/5.2/javadocs/org/hibernate/annotations/IndexColumn.html) and it works now.
我使用了新的注释 @OrderColumn 而不是 @IndexColumn (已弃用,请参阅:https://docs.jboss.org/hibernate/orm/5.2/javadocs/org/hibernate/annotations/IndexColumn.html )现在可以使用了。
Annotate one of the collections with @OrderColumn e.g.
用@OrderColumn 注释集合之一,例如
@ManyToMany(cascade = CascadeType.ALL)
@OrderColumn
private List<AddressEntity> addresses = Lists.newArrayList();
@Builder.Default
@ManyToMany(cascade = CascadeType.ALL)
private List<BankAccountEntity> bankAccounts = Lists.newArrayList();
回答by alibttb
I find using @PostLoad
annotated method in the entity most useful, I'd do something like
我发现@PostLoad
在实体中使用带注释的方法最有用,我会做类似的事情
@PostLoad
public void loadCollections(){
int s1 = productReplacements.size();
int s2 = billProductList.size();
}
this way I'm able to fine control the eager loading and initialization of collections in the same transaction that loaded the entity.
这样我就可以在加载实体的同一个事务中很好地控制集合的急切加载和初始化。