hibernate抛出multiplebagfetchexception-无法同时获取多个包

ymdaylpp  于 2021-06-30  发布在  Java
关注(0)|答案(15)|浏览(442)

hibernate在sessionfactory创建期间引发此异常:
org.hibernate.loader.multiplebagfetchexception:无法同时提取多个行李
这是我的测试用例:
父.java

@Entity
public Parent {

 @Id
 @GeneratedValue(strategy=GenerationType.IDENTITY)
 private Long id;

 @OneToMany(mappedBy="parent", fetch=FetchType.EAGER)
 // @IndexColumn(name="INDEX_COL") if I had this the problem solve but I retrieve more children than I have, one child is null.
 private List<Child> children;

}

子.java

@Entity
public Child {

 @Id
 @GeneratedValue(strategy=GenerationType.IDENTITY)
 private Long id;

 @ManyToOne
 private Parent parent;

}

这个问题怎么样?我能做什么?
编辑
好吧,我的问题是另一个“父”实体在我的父实体中,我的真实行为是:
父.java

@Entity
public Parent {

 @Id
 @GeneratedValue(strategy=GenerationType.IDENTITY)
 private Long id;

 @ManyToOne
 private AnotherParent anotherParent;

 @OneToMany(mappedBy="parent", fetch=FetchType.EAGER)
 private List<Child> children;

}

其他父级.java

@Entity
public AnotherParent {

 @Id
 @GeneratedValue(strategy=GenerationType.IDENTITY)
 private Long id;

 @OneToMany(mappedBy="parent", fetch=FetchType.EAGER)
 private List<AnotherChild> anotherChildren;

}

hibernate不喜欢有两个集合 FetchType.EAGER ,但这似乎是一个错误,我没有做不寻常的事情。。。
正在删除 FetchType.EAGERParent 或者 AnotherParent 解决了问题,但我需要它,所以真正的解决办法是使用 @LazyCollection(LazyCollectionOption.FALSE) 而不是 FetchType (感谢波佐的解决方案)。

lo8azlld

lo8azlld1#

对我来说,问题是有嵌套的急切回迁。
一种解决方案是将嵌套字段设置为lazy,并使用hibernate.initialize()加载嵌套字段:

x = session.get(ClassName.class, id);
Hibernate.initialize(x.getNestedField());
ruoxqz4g

ruoxqz4g2#

我认为hibernate的更新版本(支持jpa2.0)应该可以处理这个问题。但除此之外,您可以通过使用以下内容注解集合字段来解决此问题:

@LazyCollection(LazyCollectionOption.FALSE)

请记住移除 fetchType 属性 @*ToMany 注解。
但请注意,在大多数情况下 Set<Child> 比…更合适 List<Child> ,所以除非你真的需要 List -追求 Set 但要提醒的是,使用集合并不能消除vlad mihalcea在回答中描述的笛卡尔积!

llycmphe

llycmphe3#

我们尝试了set而不是list,这是一个噩梦:当您添加两个新对象时,equals()和hashcode()无法区分它们!因为他们没有身份证。
像eclipse这样的典型工具从数据库表生成这种代码:

@Override
public int hashCode() {
    final int prime = 31;
    int result = 1;
    result = prime * result + ((id == null) ? 0 : id.hashCode());
    return result;
}

您也可以阅读这篇文章,它正确地解释了jpa/hibernate是多么混乱。读完这篇文章,我想这是我生命中最后一次使用orm。
我也遇到过领域驱动的设计人员,他们基本上说orm是一件可怕的事情。

sxissh06

sxissh064#

要解决它只需 Set 代替 List 对于嵌套对象。

@OneToMany
Set<Your_object> objectList;

别忘了使用 fetch=FetchType.EAGER 会有用的。
还有一个概念 CollectionId 在hibernate中,如果您只想使用列表。
但要提醒的是,你不会消除下面的笛卡尔积所描述的弗拉德米哈尔恰在他的答案!

umuewwlo

umuewwlo5#

当您的saveral集合对象太复杂时,最好使用lazy,当您确实需要加载集合时,请使用: Hibernate.initialize(parent.child) 获取数据。

slmsl1lt

slmsl1lt6#

您可以使用新注解来解决此问题:

@XXXToXXX(targetEntity = XXXX.class, fetch = FetchType.LAZY)

实际上,fetch的默认值也是fetchtype.lazy。

w41d8nur

w41d8nur7#

只是从 List 键入到 Set 类型。
但要提醒的是,你不会消除下面的笛卡尔积所描述的弗拉德米哈尔恰在他的答案!

svmlkihl

svmlkihl8#

在代码中添加特定于hibernate的@fetch注解:

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

这应该可以解决与hibernate bug hhh-1718相关的问题

r6l8ljro

r6l8ljro9#

评论两者 Fetch 以及 LazyCollection 有时有助于运行项目。

@Fetch(FetchMode.JOIN)
@LazyCollection(LazyCollectionOption.FALSE)
wz3gfoph

wz3gfoph10#

在尝试了本文和其他文章中描述的每一个选项之后,我得出的结论是,解决方法如下。
在任何地方@ XXXToMany(mappedBy="parent", fetch=FetchType.EAGER) 之后不久

@Fetch(value = FetchMode.SUBSELECT)

这对我有用

bvpmtnay

bvpmtnay11#

有件好事 @LazyCollection(LazyCollectionOption.FALSE) 有几个带此注解的字段可以同时存在 FetchType.EAGER 不能,即使在这种共存是合法的情况下。
例如,一个 Order 可能有一份 OrderGroup (一个简短的)以及 Promotions (也很短)。 @LazyCollection(LazyCollectionOption.FALSE) 可用于两个,而不会引起 LazyInitializationException 都不是 MultipleBagFetchException .
对我来说 @Fetch 解决了我的问题吗 MultipleBacFetchException 但随后导致 LazyInitializationException ,臭名昭著的 no Session 错误。

zvokhttg

zvokhttg12#

我发现了一篇关于hibernate在这种对象Map中的行为的博文:http://blog.eyallupu.com/2010/06/hibernate-exception-simultaneously.html

mxg2im7a

mxg2im7a13#

您可以在jpa中保存列表,并向其中至少一个列表添加jpa annotation@ordercolumn(显然是要排序的字段的名称)。不需要特定的hibernate注解。但请记住,如果所选字段没有从0开始的值,它可能会在列表中创建空元素

[...]
 @OneToMany(mappedBy="parent", fetch=FetchType.EAGER)
 @OrderColumn(name="orderIndex")
 private List<Child> children;
 [...]

在children中,您应该添加orderindex字段

gojuced7

gojuced714#

考虑到我们有以下实体:

你想找个家长来吗 Post 实体以及所有 comments 以及 tags 收藏。
如果您使用多个 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();

hibernate将抛出臭名昭著的:

org.hibernate.loader.MultipleBagFetchException: cannot simultaneously fetch multiple bags [
  com.vladmihalcea.book.hpjp.hibernate.fetching.Post.comments,
  com.vladmihalcea.book.hpjp.hibernate.fetching.Post.tags
]

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

最糟糕的“解决方案”

现在,你会发现许多答案、博客文章、视频或其他资源告诉你使用 Set 而不是 List 为您的收藏。
那是个糟糕的建议。别那么做!
使用 Sets 而不是 Lists 将使 MultipleBagFetchException 走开,但笛卡尔积仍然存在,这实际上更糟,因为在应用这个“修复”很久之后,您就会发现性能问题。

正确的解决方案

您可以执行以下操作:

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

在第一个jpql查询中, distinct 不转到sql语句。这就是为什么我们要设定 PASS_DISTINCT_THROUGH jpa查询提示 false .
distinct在jpql中有两种含义,在这里,我们需要它来消除 getResultList 在java方面,而不是sql方面。
只要你使用 JOIN FETCH ,你会没事的。
通过使用多个查询,您将避免使用笛卡尔积,因为除了第一个集合之外的任何其他集合都是使用辅助查询获取的。

你还可以做更多

如果你用的是 FetchType.EAGER Map时的策略 @OneToMany 或者 @ManyToMany 联想,那么你很容易就有了 MultipleBagFetchException .
你最好还是从 FetchType.EAGERFetchype.LAZY 因为急于获取是一个可怕的想法,可能会导致关键的应用程序性能问题。

结论

避免 FetchType.EAGER 不要从 ListSet 只是因为这样做会让冬眠隐藏 MultipleBagFetchException 在地毯下面。一次只取一件收藏品,你就没事了。
只要在初始化集合时使用相同数量的查询,就可以了。只是不要在循环中初始化集合,因为这将触发n+1查询问题,这也会影响性能。

nfeuvbwi

nfeuvbwi15#

在我的结尾,当我有多个fetchtype.earge集合时发生了这种情况,如下所示:

@ManyToMany(fetch = FetchType.EAGER, targetEntity = className.class)
@JoinColumn(name = "myClass_id")
@JsonView(SerializationView.Summary.class)
private Collection<Model> ModelObjects;

此外,这些集合在同一列上连接。
为了解决这个问题,我将其中一个集合更改为fetchtype.lazy,因为它适合我的用例。
祝你好运~j

相关问题