Following is my code Here I am using multiple lists to fetch data from database.
On fetching data from hql query it is showing exception.
Pojo Class
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 file
<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 query
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 ";
I am trying following query to fetch data from database but this is showing
org.hibernate.loader.MultipleBagFetchException: cannot simultaneously fetch multiple bags
How to resolve this
As explained in this article, Hibernate doesn't allow fetching more than one bag because that would generate a Cartesian product.
You can change the bags to sets, and add an order-by="id"
attribute to 'simulate' an ordered list behavior:
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>
But just because you can, it doesn't mean you should.
What you could do is fetch at most one collection in the original SQL query, while the other collections are fetched afterward using secondary queries. This way you can avoid the Cartesian Product.
Another option is to use multi-level fetching from child up to parent entities.
For me I had the same error and I solved by adding the annotation of hibernate
@Fetch
@OneToMany(mappedBy="parent", fetch=FetchType.EAGER)
@Fetch(value = FetchMode.SUBSELECT)
private List<Child> childs;
You can only join-fetch following one relation for an entity (either billPaidDetailses
or billProductList
).
Consider using lazy associations and loading collections when they are needed, OR using lazy associations and loading collections manually with Hibernate.initialize(..)
. At least that was the conclusion I came to when I had a similar issue.
Either way it will take more than one query to the database.
Changing to Set
is the best solution. However, if you cannot not replace the List
with Set
(like in my case, there was a heavy use of JSF tags specific to Lists
), and if you can use Hibernate proprietary annotations, you can specify @IndexColumn (name = "INDEX_COL")
. That solution worked better for me, changing to Set
would require tons of refactoring.
So, your code would be something like this:
@IndexColumn (name = "INDEX_COL")
private List<BillPaidDetails> billPaidDetailses = new ArrayList<BillPaidDetails>();
@IndexColumn (name = "INDEX_COL")
private List<BillProduct> billProductList = new ArrayList<BillProduct>();
As Igor suggested in the comments, you could also create proxy methods to return the lists. I haven't tried that, but would be a good alternative if you cannot use Hibernate proprietary annotations.
Your request fetch too many data and HIbernate cannot load them all.
Reduce your request and/or configure your entities to retrieve just needed data