2014-07-10 13 views
7

Di seguito è riportato il mio codice Qui sto utilizzando più elenchi per recuperare i dati dal database. Durante il recupero dei dati dalla query hql sta mostrando un'eccezione.org.hibernate.loader.MultipleBagFetchException: impossibile prelevare contemporaneamente più buste

Pojo Classe

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 
} 

file di 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> 

interrogazione 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 "; 

sto cercando seguente query per recuperare i dati dal database, ma questo sta mostrando org.hibernate.loader.MultipleBagFetchException: cannot simultaneously fetch multiple bags Come risolvere e questo

+0

Hai provato cambiare le vostre liste al set? – JamesB

+0

Questo articolo può aiutarti: http://blog.eyallupu.com/2010/06/hibernate-exception-simultaneously.html – JamesB

+0

Qual è il nome della proprietà id univoca su BillProduct? – JamesB

risposta

15

Come spiegato in this article, Hibernate non consente di prelevare più di un sacchetto perché questo genererebbe un Cartesian product.

È possibile modificare le borse ai set, e aggiungere un attributo order-by="id" ad un comportamento lista 'simulare' ordinato:

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> 

Ma solo perché si può, non significa che si dovrebbe.

Quello che si potrebbe fare è recuperare al massimo una raccolta nella query SQL originale, mentre le altre raccolte vengono recuperate in seguito utilizzando le query secondarie. In questo modo puoi evitare il prodotto cartesiano.

Un'altra opzione è utilizzare multi-level fetching from child up to parent entities.

+1

Penso che questo articolo sia più pertinente al caricamento effettivo di 'Set's http://blog.eyallupu.com/2010/06/hibernate-exception-simultaneously.html Inoltre, il cambio di borse in serie non lo rende join-fetch query risulta meno di un prodotto cartesiano, che di per sé può essere un problema con le raccolte di grandi dimensioni. – enlait

0

La richiesta recupera troppi dati e HIbernate non può caricarli tutti. Ridurre la vostra richiesta e/o configurare l'entità per recuperare i dati necessari solo

2

Si può solo aderire-fetch seguendo una relazione per un'entità (sia billPaidDetailses o billProductList).

Considerare l'utilizzo di associazioni lazy e il caricamento di raccolte quando necessario, OPPURE l'uso di associazioni lazy e caricamento di raccolte manualmente con Hibernate.initialize(..). Almeno questa è stata la conclusione a cui sono arrivato quando avevo uno similar issue.

In entrambi i casi, il database richiederà più di una query.

+0

Non sono d'accordo. Esistono diversi modi per farlo senza più query, come hanno risposto altre persone e, in molti casi, la possibilità di eseguire più query genera gravi problemi di prestazioni. –

+0

@IgorDonin hai ragione, è possibile. Ma non è un proiettile d'argento per quanto riguarda le prestazioni. Immagina di dover caricare un contenitore con, diciamo 4 set di 100 elementi ciascuno. Non così tanti, giusto? La query sui risultati di join-fetch sarà 100000000 – enlait

+0

Certo, hai ragione. Ho visto casi in cui sono stati generati oltre 750000 record per riempire 600 oggetti. Ancora possibile;) –

0

Passare a Set è la soluzione migliore. Tuttavia, se non è possibile sostituire List con Set (come nel mio caso, c'era un pesante uso di tag JSF specifici per Lists) e se è possibile utilizzare annotazioni proprietarie di Hibernate, è possibile specificare @IndexColumn (name = "INDEX_COL"). Questa soluzione ha funzionato meglio per me, il passaggio a Set richiederebbe tonnellate di refactoring.

Così, il vostro codice sarebbe qualcosa di simile:

@IndexColumn (name = "INDEX_COL") 
private List<BillPaidDetails> billPaidDetailses = new ArrayList<BillPaidDetails>(); 

@IndexColumn (name = "INDEX_COL") 
private List<BillProduct> billProductList = new ArrayList<BillProduct>(); 

Come Igor suggerito nei commenti, si potrebbe anche creare metodi proxy per restituire le liste.Non l'ho provato, ma sarebbe una buona alternativa se non è possibile utilizzare le annotazioni proprietarie di Hibernate.

+0

Non necessariamente. È possibile aggiungere ulteriori metodi per i set e trasferirli agli elenchi. –

13

Per quanto mi riguarda ho avuto lo stesso errore e ho risolto aggiungendo l'annotazione di Hibernate @Fetch

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