以下は私のコードですここでは、データベースからデータを取得するために複数のリストを使用しています。 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
これを解決する方法
この記事 で説明されているように、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つのオプションは、 子から親エンティティまでのマルチレベルフェッチ を使用することです。
私にとっても同じエラーが発生し、hibernate @ Fetchの注釈を追加することで解決しました
@OneToMany(mappedBy="parent", fetch=FetchType.EAGER)
@Fetch(value = FetchMode.SUBSELECT)
private List<Child> childs;
Set
に変更するのが最善の解決策です。ただし、List
をSet
に置き換えられない場合(私の場合のように、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独自の注釈を使用できない場合の良い代替手段になります。
エンティティの1つのリレーション(billPaidDetailses
またはbillProductList
のいずれか)に続く結合フェッチのみが可能です。
必要なときに遅延関連付けを使用してコレクションをロードすることを検討してください。OR遅延関連付けを使用し、Hibernate.initialize(..)
を使用してコレクションを手動でロードします。 同様の問題 。
いずれにしても、データベースへの複数のクエリが必要になります。
@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();
エンティティで@PostLoad
アノテーション付きメソッドを使用すると最も便利で、次のようなことができます
@PostLoad
public void loadCollections(){
int s1 = productReplacements.size();
int s2 = billProductList.size();
}
このようにして、エンティティをロードしたのと同じトランザクションで、コレクションの積極的なロードと初期化を細かく制御できます。