【问题标题】:org.hibernate.loader.MultipleBagFetchException: cannot simultaneously fetch multiple bagsorg.hibernate.loader.MultipleBagFetchException:不能同时获取多个包
【发布时间】:2014-08-31 18:10:42
【问题描述】:

以下是我的代码这里我使用多个列表从数据库中获取数据。 从 hql 查询中获取数据时显示异常。

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 文件

<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查询

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 ";

我正在尝试按照查询从数据库中获取数据,但这显示 org.hibernate.loader.MultipleBagFetchException: cannot simultaneously fetch multiple bags 如何解决这个问题

【问题讨论】:

  • 您是否尝试过将列表更改为集合?
  • 这篇文章可能对你有帮助:blog.eyallupu.com/2010/06/…
  • BillProduct 上的唯一 id 属性的名称是什么?
  • Insted 的列表集正在工作。但我想处理列表
  • 你真的需要使用列表吗?

标签: java hibernate jpa hibernate-mapping bag


【解决方案1】:

您的请求获取了太多数据,而 HIbernate 无法将它们全部加载。 减少您的请求和/或配置您的实体以检索刚需要的数据

【讨论】:

    【解决方案2】:

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

    考虑使用惰性关联并在需要时加载集合,或者使用惰性关联并使用Hibernate.initialize(..) 手动加载集合。至少这是我在收到similar issue 时得出的结论。

    无论哪种方式,都需要对数据库进行多次查询。

    【讨论】:

    • 我不同意。正如其他人已经回答的那样,有几种方法可以在没有多个查询的情况下完成此任务,并且在许多情况下允许多个查询会产生严重的性能问题。
    • @IgorDonin 你是对的,这是可能的。但这并不是性能方面的灵丹妙药。想象一下,我必须加载一个容器,比如说 4 组,每组 100 个元素。没有那么多,对吧? Join-fetch 结果查询将是 100000000
    • 当然,你是对的。我见过生成超过 750000 条记录来填充 600 个对象的案例。仍然可能;)
    【解决方案3】:

    Hibernate 不允许获取多个包,因为这会生成 Cartesian product

    现在,您会发现很多答案、博客文章、视频或其他资源告诉您在收藏中使用 Set 而不是 List

    这是个糟糕的建议!

    使用Sets 代替Lists 将使MultipleBagFetchException 消失,但笛卡尔积仍然存在。

    正确的解决方法

    而不是在单个 JPQL 或 Criteria API 查询中使用多个 JOIN FETCH

    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();
    

    您可以使用以下技巧:

    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();
    

    只要您使用JOIN FETCH 最多获取一个集合,就可以了。通过使用多个查询,您将避免笛卡尔积,因为任何其他集合,但第一个集合是使用辅助查询获取的。

    【讨论】:

    • 我认为这篇文章与Sets blog.eyallupu.com/2010/06/… 的实际加载更相关而且,将bags 更改为sets 并不会使join-fetch 查询结果不再是笛卡尔积,它本身大型集合可能是个问题。
    • 最多一个可以等同于不超过一个。这意味着只有一个,因为无法查询 0.5 表。我的问题是为什么这里使用“最多一个”这个词
    • Vlad,我们在 JpaRepository 中使用 @EntityGraph 时的解决方案是什么?
    • 从该方法切换到自定义 JpaRepository 方法,该方法执行我在此答案中告诉您的操作。这就是解决方案。
    【解决方案4】:

    更改为Set 是最好的解决方案。但是,如果您不能用Set 替换List(就像在我的情况下,大量使用特定于Lists 的JSF 标签),并且如果您可以使用Hibernate 专有注释,则可以指定@987654325 @。该解决方案对我来说效果更好,更改为 Set 需要大量重构。

    所以,你的代码应该是这样的:

    @IndexColumn (name = "INDEX_COL")
    private List<BillPaidDetails> billPaidDetailses = new ArrayList<BillPaidDetails>();
    
    @IndexColumn (name = "INDEX_COL")
    private List<BillProduct> billProductList = new ArrayList<BillProduct>();
    

    正如 Igor 在 cmets 中建议的那样,您还可以创建代理方法来返回列表。我没有尝试过,但如果您不能使用 Hibernate 专有注释,那将是一个不错的选择。

    【讨论】:

    • 不一定。您可以为集合添加其他方法并将它们代理到列表中。
    【解决方案5】:

    对我来说,我遇到了同样的错误,我通过添加 hibernate 的注释来解决 @Fetch

    @OneToMany(mappedBy="parent", fetch=FetchType.EAGER)
    @Fetch(value = FetchMode.SUBSELECT)
    private List<Child> childs;
    

    【讨论】:

    • 如果我想使用 fetch type Lazy 怎么办
    【解决方案6】:

    我使用了新的注释 @OrderColumn 而不是 @IndexColumn(不推荐使用:https://docs.jboss.org/hibernate/orm/5.2/javadocs/org/hibernate/annotations/IndexColumn.html),它现在可以工作了。

    使用@OrderColumn 注释其中一个集合 例如

    @ManyToMany(cascade = CascadeType.ALL)
    @OrderColumn
    private List<AddressEntity> addresses = Lists.newArrayList();
    
    @Builder.Default
    @ManyToMany(cascade = CascadeType.ALL)
    private List<BankAccountEntity> bankAccounts = Lists.newArrayList();
    

    【讨论】:

      【解决方案7】:

      我发现在实体中使用 @PostLoad 注释方法最有用,我会做类似的事情

      @PostLoad
      public void loadCollections(){
           int s1 = productReplacements.size();
           int s2 = billProductList.size();
      }
      

      通过这种方式,我可以在加载实体的同一事务中精细控制集合的急切加载和初始化。

      【讨论】:

        猜你喜欢
        • 2015-11-28
        • 2016-09-13
        • 1970-01-01
        • 2013-05-03
        • 2011-05-19
        • 2017-08-20
        • 2016-09-27
        • 2012-03-16
        • 1970-01-01
        相关资源
        最近更新 更多