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

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-14 14:02:30  来源:igfitidea点击:

org.hibernate.loader.MultipleBagFetchException: cannot simultaneously fetch multiple bags

javahibernatejpahibernate-mappingbag

提问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 bagsHow 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 Setinstead of a Listfor your collections.

现在,您会发现很多答案、博客文章、视频或其他资源都告诉您使用 aSet而不是 aList作为您的收藏。

That's terrible advice!

这是可怕的建议!

Using Setsinstead of Listswill make the MultipleBagFetchExceptiongo away, but the Cartesian Product will still be there.

使用Sets而不是ListsMultipleBagFetchException消失,但笛卡尔积仍然存在。

The right fix

正确的修复

Instead of using multiple JOIN FETCHin 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 billPaidDetailsesor billProductList).

您只能在实体的一个关系(billPaidDetailsesbillProductList)之后加入提取。

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 Setis the best solution. However, if you cannot not replace the Listwith 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 Setwould require tons of refactoring.

改成Set是最好的解决办法。但是,如果您不能替换Listwith 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 @PostLoadannotated 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.

这样我就可以在加载实体的同一个事务中很好地控制集合的急切加载和初始化。