2009-02-26 11 views
13

Ho la seguente struttura di entità: Business -> Campagna -> Promozione, dove UN BUSINESS può avere MOLTE campagne e UNA campagna può avere MOLTE promozioni. Entrambe le relazioni uno-a-molti sono dichiarate come LAZY. Un posto nel mio codice, ho bisogno di prendere con entusiasmo entrambe le collezioni da un commercio, così faccio:JPA: per favore aiuta a capire "join fetch"

Query query = entityManager.createQuery("select b from Business b " + 
      "left join fetch b.campaigns c " + 
      "left join fetch c.promotions where b.id=:id"); 
query.setParameter("id", b.getId()); 
business = (Business) query.getResultList().get(0); 

Tuttavia, la query restituisce un elenco di risultati che contiene 4 oggetti commerciali in esso, tutti e 4 gli oggetti si riferiscono a la stessa istanza di Business. Nel mio database, questa azienda ha 3 campagne sotto di essa, e tutte e 3 le campagne hanno 3 promozioni sotto di loro.

Ho due domande:

  1. In un primo momento, ho usare List per contiene i molti lati di relazioni, ma quando viene eseguito il programma, io ottenere "org.hibernate.HibernateException: non può allo stesso tempo recuperare più borse " eccezione. Poi ho cercato su Google questa eccezione e sembra che debba usare Set, invece di List. Così ho cambiato la collezione in Set e ha funzionato. Qualcuno può dirmi perché Lista non funzionerà in questa situazione?

  2. Mi aspetto che la query restituisca un singolo risultato, perché sta interrogando l'id, che è la chiave primaria e quindi dovrebbe restituire un solo risultato. Ma si scopre che restituisce 4 istanze in un elenco. È un problema? O è questo comportamento previsto?

Qualsiasi aiuto sarà molto apprezzato.

risposta

20

l'SQL generato sarebbe simile:

select * from Business b 
left outer join campaigns c on c.business_id = b.id 
left join promotions p on p.campaign_id = c.id 
where b.id=:id 

Internamente Hibernate avrà una sola istanza di Business, tuttavia i duplicati verranno conservati nel set di risultati. Questo è un comportamento previsto. Il comportamento desiderata può essere acheived sia utilizzando la clausola DISTINCT, oppure utilizzando un LinkedHashSet per filtrare i risultati:

Collection result = new LinkedHashSet(query.getResultList()); 

che restituirà solo risultati unici, preservando ordine di inserimento.

"org.hibernate.HibernateException: impossibile prelevare contemporaneamente più buste" si verifica ogni volta che si tenta di recuperare più di una raccolta in modo ordinato (ed eventualmente elementi duplicati). Questo ha un senso se si considera l'SQL generato. Hibernate non ha modo di sapere se un oggetto duplicato è stato causato dal join o da effettivi dati duplicati nella tabella figlio. Guarda this per una buona spiegazione.

+1

L'aggiunta di "distinto" alla query ha fatto il trucco. Grazie. –

1
  1. Presumo se si utilizza un elenco, Hibernate assume che l'ordine è importante, e sarà dedurre l'ordine degli elementi dalle righe restituite, ma se si partecipa con un Hibernate tavolo nother otterrà ogni multiplo campagna tempi che confondono l'ibernazione. Ancora una volta, sto solo presupponendo che questo

  2. Questo comportamento è previsto Utilizzare un RootEntityResultTransformer per ottenere una singola entità root: http://www.hibernate.org/hib_docs/v3/api/org/hibernate/transform/RootEntityResultTransformer.html

3
  1. lista può avere elementi ridondanti a causa del prodotto cartesiano e le persone non erano consapevoli di questo, così i ragazzi ibernazione iniziato a gettare questo errore per costringere la gente ad utilizzare Set invece di Lista per evitare questo problema. Fonte 1
0

Un'altra soluzione, invece di utilizzare Set è quello di utilizzare due query per ottenere i dati: - 1 ° uno per caricare la campagna e le sue promozioni associati del business. Quindi caricare l'azienda e le sue campagne utilizzando fetch

Query query1 = entityManager.createQuery("select c from Business b join b.campaigns c left join fetch c.promotions where b.id=:id"); 
    query1.setParameter("id", b.getId()); 
    query1.getResultList(); 

    Query query2 = entityManager.createQuery("select b from Business b left join fetch  b.campaigns where b.id=:id"); 
    query2.setParameter("id", b.getId()); 
    business = (Business) query2.getResultList().get(0); 
Problemi correlati