Java org.hibernate.loader.MultipleBagFetchException:无法同时提取多个行李

Java org.hibernate.loader.MultipleBagFetchException:无法同时提取多个行李,java,hibernate,jpa,hibernate-mapping,bag,Java,Hibernate,Jpa,Hibernate Mapping,Bag,下面是我的代码,我使用多个列表从数据库中获取数据。 从hql查询获取数据时,它显示异常 波乔班 public class BillDetails implements java.io.Serializable { private Long billNo; // other fields @LazyCollection(LazyCollectionOption.FALSE) private List<BillPaidDetails> billPaidDetailses = new A

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

波乔班

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
}
我正在尝试下面的查询从数据库中获取数据,但这显示
org.hibernate.loader.MultipleBagFetchException:无法同时提取多个行李

如何解决此问题

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

对于一个实体,您只能加入以下一个关系(无论是
BillPaidDetails
还是
billProductList

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


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

Hibernate不允许获取多个行李,因为这样会生成一个查询

现在,你会发现很多答案、博客帖子、视频或其他资源告诉你使用
Set
而不是
List
来收藏

这是个糟糕的建议

使用
集合
而不是
列表
将使
多重回溯异常
消失,但笛卡尔积仍然存在

正确的解决方案 在一个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 posts=entityManager.createQuery(“”)
选择p
从邮政p
左连接获取p.comments
左连接获取p标记
其中p.id介于:minId和:maxId之间
“”,Post.class)
.setParameter(“最小值”,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();
List posts=entityManager.createQuery(“”)
选择不同的p
从邮政p
左连接获取p.comments
其中p.id介于:minId和:maxId之间
“”,Post.class)
.setParameter(“最小值”,1L)
.setParameter(“maxId”,50L)
.setHint(QueryHints.PASS\u DISTINCT\u THROUGH,false)
.getResultList();
posts=entityManager.createQuery(“”)
选择不同的p
从邮政p
左连接获取p.t标记
其中p在:posts中
“”,Post.class)
.setParameter(“posts”,posts)
.setHint(QueryHints.PASS\u DISTINCT\u THROUGH,false)
.getResultList();

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

更改为
是最佳解决方案。但是,如果不能将
列表
替换为
(就像在我的例子中,大量使用了特定于
列表
)的JSF标记),并且如果可以使用Hibernate专有注释,则可以指定
@IndexColumn(name=“INDEX\u COL”)
。这个解决方案对我来说效果更好,更改为
Set
需要大量的重构

因此,您的代码如下所示:

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

@IndexColumn (name = "INDEX_COL")
private List<BillProduct> billProductList = new ArrayList<BillProduct>();
@IndexColumn(name=“INDEX\u COL”)
私有列表BillPaidDetails=new ArrayList();
@索引列(name=“INDEX\u COL”)
私有列表billProductList=new ArrayList();

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

对我来说,我也有同样的错误,我通过添加Hibernate注释解决了这个问题 @Fetch

@OneToMany(mappedBy="parent", fetch=FetchType.EAGER)
@Fetch(value = FetchMode.SUBSELECT)
private List<Child> childs;
@OneToMany(mappedBy=“parent”,fetch=FetchType.EAGER)
@Fetch(值=FetchMode.SUBSELECT)
私人名单儿童;

我使用了新的注释@OrderColumn而不是@IndexColumn(不推荐使用,请参见:),现在可以使用了

用@OrderColumn注释其中一个集合 e、 g

@ManyToMany(cascade=CascadeType.ALL)
@订单列
私有列表地址=Lists.newArrayList();
@Builder.Default
@多个(级联=级联类型.ALL)
private List bankAccounts=Lists.newArrayList();

我发现在实体中使用
@PostLoad
带注释的方法最有用,我会这样做

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

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

您是否尝试过将列表更改为集合?本文可能会帮助您:BillProduct上的唯一id属性的名称是什么?而不是列表集合正在工作。但是我想用列表你真的需要使用列表吗?不一定。你可以为集合添加其他方法,并将它们代理到列表中。我不同意。正如其他人所回答的那样,有几种方法可以在没有多个查询的情况下实现这一点,并且允许多个查询在许多情况下会产生严重的性能问题。@IgorDonin如果你是对的,这是可能的。但就性能而言,这并不是一个灵丹妙药。假设我必须装载一个容器,假设每个容器有4组100个元素。不是很多吧?联接获取结果查询将为100000000确定,您是对的。我见过这样的情况,即生成了超过750000条记录,填充了600个对象。仍然可能;)我认为本文与
Set
s的实际加载更相关,而且,将包更改为集合并不会使连接获取查询结果减少笛卡尔积,这本身可能是大型集合的一个问题。最多一个可以等同于不超过一个。这意味着只有一个,因为无法查询0.5表。我的问题是为什么在这里使用“最多一个”这个词,那么当我们在JpaRepository中使用@EntityGraph时,解决方案是什么呢?从这个问题切换到一个自定义JpaRepository方法,按照我在这个答案中告诉你的做。这就是解决办法。如果我
@OneToMany(mappedBy="parent", fetch=FetchType.EAGER)
@Fetch(value = FetchMode.SUBSELECT)
private List<Child> childs;
@ManyToMany(cascade = CascadeType.ALL)
@OrderColumn
private List<AddressEntity> addresses = Lists.newArrayList();

@Builder.Default
@ManyToMany(cascade = CascadeType.ALL)
private List<BankAccountEntity> bankAccounts = Lists.newArrayList();
@PostLoad
public void loadCollections(){
     int s1 = productReplacements.size();
     int s2 = billProductList.size();
}