2009-10-21 3 views
5

La implementing-result-paging-in-hibernate-getting-total-number-of-rows domanda innescare un'altra domanda per me, su qualche implementazione preoccupazione:Java codifica best practice per il riutilizzo di parte di una query per contare

Ora sai di avere di riutilizzare parte della query HQL fare il conteggio, come riutilizzare in modo efficiente?

Le differenze tra le due query HQL sono:

  1. la selezione è count(?), al posto del POJO o proprietà (o una lista di)
  2. i recuperi non dovrebbe succedere, in modo da alcune tabelle non dovrebbero essere unito
  3. il order by dovrebbe scomparire

esistono altre differenze?

Avete codifica migliori pratiche per raggiungere questo obiettivo il riutilizzo efficiente (riguarda: lo sforzo, la chiarezza, performance)?

Esempio per una semplice query HQL:

select  a  from A a join fetch a.b b where a.id=66 order by a.name 
    select count(a.id) from A a     where a.id=66 

AGGIORNATO

Ho ricevuto risposte su:

  • utilizzando Criteri (ma usiamo HQL per lo più)
  • manipolare la stringa query (ma tutti sono d'accordo sembra complicato e non molto sicuro)
  • avvolgendo la query, basandosi su ottimizzazione del database (ma c'è la sensazione che questo non è sicuro)

Speravo che qualcuno avrebbe dato le opzioni lungo un altro percorso, più legato alla concatenazione delle stringhe.
È possibile creare entrambe le query HQL utilizzando le parti comuni?

risposta

1

Nizza domanda. Ecco quello che ho fatto in passato (molte cose che hai citato già):

  1. Verificare se SELEZIONA clausola è presente.
    1. Se non lo è, aggiungere select count(*)
    2. In caso contrario verificare se ha DISTINCT o aggregare funzioni in esso. Se stai usando ANTLR per analizzare la tua query, è possibile aggirare quelli, ma è piuttosto complicato. È molto probabile che meglio solo avvolgere il tutto con select count(*) from().
  2. Rimuovere fetch all properties
  3. Rimuovere fetch da unisce se si sta analizzando HQL come stringa. Se siete veramente l'analisi della query con ANTLR è possibile rimuovere left join del tutto; è piuttosto complicato controllare tutti i riferimenti possibili.
  4. Rimuovere order by
  5. A seconda di quello che hai fatto in 1.2 è necessario rimuovere/modificare group by/having.

Quanto sopra si applica HQL, naturalmente. Per le query di criteri, sei abbastanza limitato con ciò che puoi fare perché non si presta facilmente alla manipolazione. Se si utilizza una sorta di strato wrapper in cima ai criteri, si otterrà l'equivalente del sottoinsieme (limitato) dei risultati di analisi ANTLR e potrebbe applicare la maggior parte di quanto sopra in questo caso.

Dato che normalmente si terrebbe l'offset della pagina corrente e il conteggio totale, di solito eseguo la query effettiva con il limite/offset dato e eseguo solo la query count(*) se il numero di risultati restituiti è maggiore o uguale a limite E l'offset è zero (in tutti gli altri casi ho già eseguito il count(*) prima o ho comunque ottenuto tutti i risultati). Questo è un approccio ottimistico per quanto riguarda le modifiche concorrenti, ovviamente.

Aggiornamento (su HQL-assemblaggio a mano)

non mi piace particolarmente questo approccio. Quando mappato come query con nome, HQL ha il vantaggio del controllo degli errori in fase di compilazione (beh, il tempo di esecuzione tecnicamente, perché SessionFactory deve essere compilato anche se di solito è fatto durante i test di integrazione). Quando generato in fase di runtime fallisce in fase di esecuzione :-) Anche fare ottimizzazioni delle prestazioni non è esattamente facile.

Lo stesso ragionamento si applica ai criteri, ovviamente, ma è un po 'più difficile da rovinare a causa dell'API ben definita rispetto alla concatenazione di stringhe. Costruire due query HQL in parallelo (una paginata e una "conteggio globale") porta anche alla duplicazione del codice (e potenzialmente a più bachi) o ti costringe a scrivere una specie di strato wrapper in cima per farlo per te. Entrambi i modi sono lontani dall'ideale. E se hai bisogno di fare questo dal codice client (come in oltre API), il problema diventa ancora peggio.

In realtà ho pondered quite a bit su questo problema. L'API di ricerca da Hibernate-Generic-DAO sembra un ragionevole compromesso; ci sono più dettagli nella mia risposta alla domanda collegata sopra.

+0

+1 Grazie per queste precisioni sulla manipolazione della query. Grazie anche per l'eccellente precisione che ** una query di conteggio dovrebbe essere eseguita solo dopo una prima query **. – KLE

+0

Ho aggiornato la mia domanda, vorresti fare un'altra risposta relativa alla nuova parte? Mi sono piaciuti molti dei tuoi post di altri, e tu sei un esperto di java :-) ... – KLE

+0

Grazie :-) Ho aggiornato la mia risposta sopra. – ChssPly76

2

Beh, non sono sicuro che questo è un best-practice, ma è il mio-pratica :)

Se ho come interrogazione qualcosa di simile:

select A.f1,A.f2,A.f3 from A, B where A.f2=B.f2 order by A.f1, B.f3 

E voglio solo sapere quanti risultati otterranno, eseguo:

select count(*) from (select A.f1, ... order by A.f1, B.f3) 

e quindi ottenere il risultato come un intero, senza risultati della mappatura in un POJO.

Analizza la query per rimuovere alcune parti, ad esempio 'ordinare per' è molto complicato. Un buon RDBMS ottimizzerà la tua query per te.

Buona domanda.

+0

Grazie per il supporto :-) La mia query tipica è HQL, restituendo pojos o un elenco di proprietà. – KLE

+0

Come te, non ho voglia di analizzare la query per rimuovere alcune parti; ma non mi fido davvero dell'RDBMS per ottimizzare la query in tutti i casi. Penso che alcuni casi siano incasinati, ed è difficile prevederli. ** Esistono degli elenchi di fatti relativi a queste ottimizzazioni? ** – KLE

+0

Non so dove trovare questi elenchi di fatti o se questa informazione è pubblica. Non posso essere completamente sicuro che un RDBMS ottimizzi in questo modo. Ma da studente, ho visto alcune ottimizzazioni "vecchie e fondamentali" che sembrano più difficili di questa. In questo caso, ad es. RDBMS penserebbe: "Mi stanno interrogando per un numero di righe, quindi l'ordine è evitabile". – sinuhepop

2

Hai provato fare le intenzioni di Hibernate impostando una proiezione sul vostro (SQL?) Criteri? Sono stato per lo più sulla base di criteri, quindi non sono sicuro di come questo è applicabile al vostro caso, ma ho usato

getSession().createCriteria(persistentClass). 
setProjection(Projections.rowCount()).uniqueResult() 

e lasciando Hibernate capire la roba caching/riuso/smart da solo .. Non sono proprio sicuro di quanta roba sia intelligente in realtà ... Qualcuno dovrebbe fare commenti su questo?

+0

Hibernate non memorizza automaticamente le query nella cache; devi farlo in modo esplicito. Il problema con l'approccio sopra (e l'uso dei Criteri in generale) è che il livello che assembla i criteri deve crearne un'altra copia solo per il conteggio. In altre parole, non posso semplicemente creare un criterio nel livello aziendale, passarlo al servizio (o DAO) e tornare indietro di una pagina di risultati + conteggio totale. Non è un grosso problema per le piccole app ma porta un sacco di codice non necessario in quelle più grandi. – ChssPly76

0

In una situazione a mano libera HQL Vorrei usare qualcosa di simile, ma questo non è riutilizzabile in quanto è molto specifica per il dato entità

Integer count = (Integer) session.createQuery("select count(*) from ....").uniqueResult(); 

Fate questo una volta e regolare di partenza il numero di conseguenza fino a scorrere.

Per i criteri di se io uso un campione come questo

final Criteria criteria = session.createCriteria(clazz); 
      List<Criterion> restrictions = factory.assemble(command.getFilter()); 
      for (Criterion restriction : restrictions) 
       criteria.add(restriction); 
      criteria.add(Restrictions.conjunction()); 
      if(this.projections != null) 
       criteria.setProjection(factory.loadProjections(this.projections)); 
      criteria.addOrder(command.getDir().equals("ASC")?Order.asc(command.getSort()):Order.desc(command.getSort())); 
      ScrollableResults scrollable = criteria.scroll(ScrollMode.SCROLL_INSENSITIVE); 
      if(scrollable.last()){//returns true if there is a resultset 
       genericDTO.setTotalCount(scrollable.getRowNumber() + 1); 
       criteria.setFirstResult(command.getStart()) 
         .setMaxResults(command.getLimit()); 
       genericDTO.setLineItems(Collections.unmodifiableList(criteria.list())); 
      } 
      scrollable.close(); 
      return genericDTO; 

Ma questo il conteggio ogni volta chiamando ScrollableResults:last().