21

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

4

7 に答える 7

31

Hibernate では、デカルト積が生成されるため、複数のバッグをフェッチすることはできません。

Set現在、コレクションに の代わりにを使用するように指示する多くの回答、ブログ投稿、ビデオ、またはその他のリソースを見つけることができます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();

を使用して最大で 1 つのコレクションをフェッチする限り、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が最善の解決策です。Listただし、をに置き換えることができないSet場合 (私の場合のように、 に固有の 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

エンティティの 1 つのリレーション ( または のいずれbillPaidDetailsesbillProductList) に従ってのみ結合フェッチできます。

遅延関連付けを使用して必要に応じてコレクションをロードするか、遅延関連付けを使用してコレクションを手動でロードすることを検討してくださいHibernate.initialize(..)。少なくとも、それは私が同様の問題を抱えたときに私が到達した結論でした.

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

于 2014-07-10T11:56:37.637 に答える