web-dev-qa-db-ja.com

org.hibernate.loader.MultipleBagFetchException:複数のバッグを同時にフェッチすることはできません

以下は私のコードですここでは、データベースからデータを取得するために複数のリストを使用しています。 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
}

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これを解決する方法

12
xrcwrn

この記事 で説明されているように、Hibernateは デカルト積 を生成するため、複数のバッグをフェッチできません。

バッグをセットに変更し、order-by="id"属性は、順序付きリストの動作を「シミュレート」します。

private Set<BillPaidDetails> billPaidDetailses = new LinkedHashSet<>();
private Set<BillProduct> billProductList = new LinkedHashSet<>();

<set name="billProductList" table="bill_product" 
     inverse="true" lazy="false" fetch="join" order-by="id">
    <key>
        <column name="bill_no" not-null="true" />
    </key>
    <one-to-many class="iland.hbm.BillProduct" />
</set>

<set name="billPaidDetailses" table="bill_paid_details" 
     inverse="true" lazy="false" fetch="select" order-by="id">
    <key>
        <column name="bill_no" not-null="true" />
    </key>
    <one-to-many class="iland.hbm.BillPaidDetails" />
</set>

しかし、できるからといって、そうすべきだというわけではありません。

できることは、元のSQLクエリで最大で1つのコレクションをフェッチすることです。他のコレクションは、セカンダリクエリを使用して後でフェッチされます。これにより、デカルト積を回避できます。

もう1つのオプションは、 子から親エンティティまでのマルチレベルフェッチ を使用することです。

20
Vlad Mihalcea

私にとっても同じエラーが発生し、hibernate @ Fetchの注釈を追加することで解決しました

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

Setに変更するのが最善の解決策です。ただし、ListSetに置き換えられない場合(私の場合のように、Listsに固有のJSFタグが頻繁に使用されていました)、使用できる場合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独自の注釈を使用できない場合の良い代替手段になります。

2
L. Holanda

エンティティの1つのリレーション(billPaidDetailsesまたはbillProductListのいずれか)に続く結合フェッチのみが可能です。

必要なときに遅延関連付けを使用してコレクションをロードすることを検討してください。OR遅延関連付けを使用し、Hibernate.initialize(..)を使用してコレクションを手動でロードします。 同様の問題

いずれにしても、データベースへの複数のクエリが必要になります。

2
enlait

@IndexColumnの代わりに新しいアノテーション@OrderColumnを使用しました(非推奨: https://docs.jboss.org/hibernate/orm/5.2/javadocs/org/hibernate/annotations/IndexColumn.html を参照)そしてそれは今動作します。

コレクションの1つに@OrderColumnで注釈を付けます。

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

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

エンティティで@PostLoadアノテーション付きメソッドを使用すると最も便利で、次のようなことができます

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

このようにして、エンティティをロードしたのと同じトランザクションで、コレクションの積極的なロードと初期化を細かく制御できます。

0
alibttb