Domanda

Quali alternative devo implementare una query di unione usando l'ibernazione? So che l'ibernazione al momento non supporta le query sui sindacati, in questo momento l'unico modo in cui vedo per creare un sindacato è utilizzare una tabella di visualizzazione.

L'altra opzione è usare jdbc semplice, ma in questo modo perderei tutti i miei goodies di query di esempio / criterio, così come la convalida del mapping di ibernazione che hibernate esegue rispetto alle tabelle / colonne.

È stato utile?

Soluzione

Usa VISUALIZZA. Le stesse classi possono essere mappate su diverse tabelle / viste usando il nome dell'entità, quindi non avrai nemmeno una duplicazione. Essere lì, fatto, funziona bene.

Il semplice JDBC ha un altro problema nascosto: non è a conoscenza della cache della sessione di Hibernate, quindi se qualcosa viene memorizzato nella cache fino alla fine della transazione e non cancellato dalla sessione di Hibernate, la query JDBC non la troverà. A volte potrebbe essere molto sconcertante.

Altri suggerimenti

Puoi usare id in (select id from ...) or id in (select id from ...)

es. anziché non funzionante

from Person p where p.name="Joe"
union
from Person p join p.children c where c.name="Joe"

potresti farlo

from Person p 
  where p.id in (select p1.id from Person p1 where p1.name="Joe") 
    or p.id in (select p2.id from Person p2 join p2.children c where c.name="Joe");

Almeno usando MySQL, però, ti imbatterai in problemi di prestazioni con esso in seguito. A volte è più facile invece fare un join di un uomo povero su due query:

// use set for uniqueness
Set<Person> people = new HashSet<Person>((List<Person>) query1.list());
people.addAll((List<Person>) query2.list());
return new ArrayList<Person>(people);

Spesso è meglio fare due semplici query piuttosto che una complessa.

EDIT:

per fare un esempio, ecco l'output EXPLAIN della query MySQL risultante dalla soluzione subselect:

mysql> explain 
  select p.* from PERSON p 
    where p.id in (select p1.id from PERSON p1 where p1.name = "Joe") 
      or p.id in (select p2.id from PERSON p2 
        join CHILDREN c on p2.id = c.parent where c.name="Joe") \G
*************************** 1. row ***************************
           id: 1
  select_type: PRIMARY
        table: a
         type: ALL
possible_keys: NULL
          key: NULL
      key_len: NULL
          ref: NULL
         rows: 247554
        Extra: Using where
*************************** 2. row ***************************
           id: 3
  select_type: DEPENDENT SUBQUERY
        table: NULL
         type: NULL
possible_keys: NULL
          key: NULL
      key_len: NULL
          ref: NULL
         rows: NULL
        Extra: Impossible WHERE noticed after reading const tables
*************************** 3. row ***************************
           id: 2
  select_type: DEPENDENT SUBQUERY
        table: a1
         type: unique_subquery
possible_keys: PRIMARY,name,sortname
          key: PRIMARY
      key_len: 4
          ref: func
         rows: 1
        Extra: Using where
3 rows in set (0.00 sec)

Soprattutto, 1. row non utilizza alcun indice e considera 200k + righe. Male! L'esecuzione di questa query ha richiesto 0,7 secondi in cui entrambe le sottoquery sono espresse in millisecondi.

Sono d'accordo con Vladimir. Anch'io ho esaminato l'uso di UNION in HQL e non sono riuscito a trovare un modo per aggirarlo. La cosa strana è che ho potuto trovare (nelle FAQ di Hibernate) che UNION non è supportato, segnalazioni di bug relative a UNION contrassegnate come "riparate", newsgroup di persone che affermano che le dichiarazioni verrebbero troncate a UNION e altri newsgroup di persone che segnalano che funziona bene ... Dopo una giornata passata inosservata, ho finito per riportare il mio HQL su SQL semplice, ma farlo in una vista nel database sarebbe una buona opzione. Nel mio caso, parti della query sono state generate dinamicamente, quindi ho dovuto creare l'SQL nel codice.

Ho una soluzione per uno scenario critico (per il quale ho lottato molto) con l'unione in HQL.

per es. Invece di non funzionare: -

select i , j from A a  , (select i , j from B union select i , j from C) d where a.i = d.i 

o

select i , j from A a  JOIN (select i , j from B union select i , j from C) d on a.i = d.i 

Potresti farlo in Hibernate HQL - >

Query q1 =session.createQuery(select i , j from A a JOIN B b on a.i = b.i)
List l1 = q1.list();

Query q2 = session.createQuery(select i , j from A a JOIN C b on a.i = b.i)
List l2 = q2.list();

allora puoi aggiungere entrambi gli elenchi - >

l1.addAll(l2);

Una vista è un approccio migliore, ma poiché hql in genere restituisce un elenco o un set ... puoi fare list_1.addAll (list_2). Fa assolutamente schifo rispetto a un sindacato ma dovrebbe funzionare.

Forse ho avuto un problema più diretto da risolvere. Il mio "per esempio" era in JPA con Hibernate come provider JPA.

Ho diviso le tre selezioni (due in un secondo caso) in selezioni multiple e ho combinato le raccolte restituite da me, sostituendo efficacemente un "unione tutto".

Anch'io ho sofferto di questo dolore - se la query viene generata in modo dinamico (ad esempio Criteri di ibernazione), non riuscirei a trovare un modo pratico per farlo.

La buona notizia per me era che stavo solo studiando l'unione per risolvere un problema di prestazioni quando usavo un 'o' in un database Oracle.

La soluzione che Patrick ha pubblicato (combinando i risultati in modo programmatico usando un set) mentre brutta (specialmente perché volevo fare anche il paging dei risultati) era adeguata per me.


Come ha detto Patrick, aggiungere gli ELENCO da ogni SELEZIONA sarebbe una buona idea, ma ricorda che si comporta come UNIONE TUTTO . Per evitare questo effetto collaterale, basta controllare se l'oggetto è già stato aggiunto nella raccolta finale o meno. Se no, quindi aggiungilo.
Qualcos'altro di cui dovresti preoccuparti è che se hai UNISCITI in ogni SELEZIONA , il risultato sarebbe un elenco di array di oggetti ( Elenca < Objetc [] > ) in modo da dover iterare su di esso per mantenere solo l'oggetto di cui hai bisogno.
Spero che funzioni.

Ecco un caso speciale, ma potrebbe ispirarti a creare il tuo lavoro intorno. L'obiettivo qui è contare il numero totale di record da due diverse tabelle in cui i record soddisfano un determinato criterio. Credo che questa tecnica funzionerà in ogni caso in cui è necessario aggregare dati da più tabelle / fonti.

Ho un'impostazione speciale per le classi intermedie, quindi il codice che chiama la query con nome è breve e dolce, ma puoi usare qualsiasi metodo che usi normalmente insieme alle query con nome per eseguire la query.

QueryParms parms=new QueryParms();
parms.put("PROCDATE",PROCDATE);

Long pixelAll = ((SourceCount)Fetch.row("PIXEL_ALL",parms,logger)).getCOUNT();

Come puoi vedere qui, la query denominata inizia ad assomigliare molto a una dichiarazione sindacale:

@Entity
@NamedQueries({
        @NamedQuery(
            name  ="PIXEL_ALL",
            query = "" +
                    "  SELECT new SourceCount(" +
                    "     (select count(a) from PIXEL_LOG_CURR1 a " +
                    "       where to_char(a.TIMESTAMP, 'YYYYMMDD') = :PROCDATE " +
                    "     )," +
                    "     (select count(b) from PIXEL_LOG_CURR2 b" +
                    "       where to_char(b.TIMESTAMP, 'YYYYMMDD') = :PROCDATE " +
                    "     )" +
                    ") from Dual1" +
                    ""
    )
})

public class SourceCount {
    @Id
    private Long   COUNT;

    public SourceCount(Long COUNT1, Long COUNT2) {
        this.COUNT = COUNT1+COUNT2;
    }

    public Long getCOUNT() {
        return COUNT;
    }

    public void setCOUNT(Long COUNT) {
        this.COUNT = COUNT;
    }
}

Parte della magia qui è creare una tabella fittizia e inserire un record in essa. Nel mio caso, l'ho chiamato dual1 perché il mio database è Oracle, ma non credo sia importante come si chiama la tabella fittizia.

@Entity
@Table(name="DUAL1")
public class Dual1 {
    @Id
    Long ID;
}

Non dimenticare di inserire il tuo record fittizio:

SQL> insert into dual1 values (1);
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top