21

以下是我的代码这里我使用多个列表从数据库中获取数据。在从 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 如何解决这个问题

4

7 回答 7

31

Hibernate 不允许获取多个包,因为这会生成笛卡尔积

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

这是可怕的建议!

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

正确的修复

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

您可以执行以下技巧:

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,就可以了。通过使用多个查询,您将避免笛卡尔积,因为任何其他集合,但第一个集合是使用辅助查询获取的。

于 2014-07-10T12:39:22.380 回答
28

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

@OneToMany(mappedBy="parent", fetch=FetchType.EAGER)
@Fetch(value = FetchMode.SUBSELECT)
private List<Child> childs;
于 2015-07-19T16:24:12.897 回答
2

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

因此,您的代码将是这样的:

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

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

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

于 2014-10-15T22:22:51.127 回答
2

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

考虑使用延迟关联并在需要时加载集合,或者使用延迟关联并手动加载集合Hibernate.initialize(..)。至少这是我在遇到类似问题时得出的结论。

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

于 2014-07-10T11:56:37.637 回答
1

I find using @PostLoad annotated method in the entity most useful, I'd do something like

@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.

于 2019-08-28T19:06:47.900 回答
0

我使用了新的注释 @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();
于 2019-06-25T12:11:39.720 回答
-3

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

于 2014-07-10T11:33:00.270 回答