549

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;

}

Child.java

@Entity
public Child {

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

 @ManyToOne
 private Parent parent;

}

この問題はどうですか?私に何ができる?


編集

OK、私が抱えている問題は、別の「親」エンティティが親の中にあることです。私の実際の動作は次のとおりです。

親.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 は で 2 つのコレクションが好きではありませんFetchType.EAGERが、これはバグのようです。私は異常なことをしていません...

FetchType.EAGER問題から削除ParentまたはAnotherParent解決しますが、私はそれが必要なので、実際の解決策は@LazyCollection(LazyCollectionOption.FALSE)代わりに使用することです(解決策についてBozhoFetchTypeに感謝します)。

4

18 に答える 18

632

休止状態の新しいバージョン (JPA 2.0 をサポート) がこれを処理する必要があると思います。ただし、それ以外の場合は、コレクション フィールドに次の注釈を付けることで回避できます。

@LazyCollection(LazyCollectionOption.FALSE)

注釈fetchTypeから属性を忘れずに削除してください。@*ToMany

ただし、ほとんどの場合、 aSet<Child>は よりも適切であることに注意してList<Child>ください。ListSet

ただし、セットを使用しても、 Vlad Mihalcea の回答で説明されているように、下にあるデカルト積を削除できないことに注意してください。

于 2010-12-02T13:30:30.943 に答える
307

Listタイプからタイプに変更するだけSetです。

ただし、 Vlad Mihalcea の回答で説明されているように、下にあるデカルト積を削除しないことに注意してください。

于 2011-05-03T05:55:07.050 に答える
176

Hibernate 固有の @Fetch アノテーションをコードに追加します。

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

これにより、Hibernate バグHHH-1718に関連する問題が修正されるはずです。

于 2011-11-29T10:46:02.507 に答える
140

次のエンティティがあるとします。

ここに画像の説明を入力

そして、すべてのおよびコレクションPostとともにいくつかの親エンティティを取得したいと考えています。commentstags

複数の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_THROUGHJPA クエリ ヒントを に設定しますfalse

getResultListDISTINCT には JPQL で 2 つの意味があり、ここでは SQL 側ではなく Java 側で返される Java オブジェクト参照を重複排除する必要があります。

を使用して最大で 1 つのコレクションをフェッチする限り、JOIN FETCH問題ありません。

複数のクエリを使用することで、他のコレクションではなく最初のコレクションがセカンダリ クエリを使用してフェッチされるため、デカルト積を回避できます。

もっとできることがあります

またはアソシエーションFetchType.EAGERのマッピング時に戦略を使用している場合、簡単に.@OneToMany@ManyToManyMultipleBagFetchException

熱心なフェッチはアプリケーションのパフォーマンスに重大な問題を引き起こす可能性があるため、からFetchType.EAGERに切り替えることをお勧めします。Fetchype.LAZY

結論

Hibernateがカーペットの下に隠れてしまうという理由だけで、からにFetchType.EAGER切り替えないでください。一度に 1 つのコレクションだけをフェッチすれば問題ありません。ListSetMultipleBagFetchException

初期化するコレクションと同じ数のクエリを実行する限り、問題ありません。ループでコレクションを初期化しないでください。これにより、N+1 クエリの問題が発生し、パフォーマンスも低下します。

于 2018-06-27T06:01:16.863 に答える
9

この種のオブジェクト マッピングにおける Hibernate の動作に関する良いブログ記事を見つけました: http://blog.eyallupu.com/2010/06/hibernate-exception-simultaneously.html

于 2013-01-11T15:52:34.270 に答える
6

ブース EAGER リストを JPA に保持し、それらの少なくとも 1 つに JPA アノテーション@OrderColumnを追加できます(明らかに、注文するフィールドの名前を使用します)。特定の休止状態の注釈は必要ありません。ただし、選択したフィールドに 0 から始まる値がない場合、リストに空の要素が作成される可能性があることに注意してください。

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

Children に orderIndex フィールドを追加する必要があります

于 2017-04-25T08:53:08.170 に答える
2

Listの代わりにSetを試してみましたが、これは悪夢です: 2 つの新しいオブジェクトを追加すると、equals () とhashCode () は両方を区別できません! 彼らはIDを持っていないからです。

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 はひどいものだと言うドメイン駆動設計担当者にも遭遇しました。

于 2018-01-10T13:52:56.167 に答える
1

saveral コレクションを使用する複雑すぎるオブジェクトがある場合、それらすべてを EAGER fetchType で使用するのは得策ではありません。LAZY を使用することをお勧めします。コレクションをロードする必要がある場合は、次を使用Hibernate.initialize(parent.child)してデータをフェッチします。

于 2015-10-15T13:38:21.040 に答える
0

私の最後では、これは、次のように FetchType.EAGER を使用して複数のコレクションを作成したときに発生しました。

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

さらに、コレクションは同じ列に結合していました。

この問題を解決するために、コレクションの 1 つを FetchType.LAZY に変更しました。これは私のユース ケースでは問題ないためです。

幸運を!~J

于 2020-01-11T21:14:35.337 に答える