Domanda

Il Implement-result-paging-in- hibernate-getting-total-numero-di-righe fa scattare un'altra domanda per me, riguardo a alcuni problemi di implementazione :

Ora sai che devi riutilizzare parte della query HQL per fare il conteggio, come riutilizzare in modo efficiente?

Le differenze tra le due query HQL sono:

  1. la selezione è count (?) , anziché il pojo o la proprietà (o l'elenco di)
  2. i recuperi non dovrebbero avvenire, quindi alcune tabelle non dovrebbero essere unite
  3. l'ordine per dovrebbe scomparire

Ci sono altre differenze?

Hai migliori pratiche di codifica per raggiungere questo riutilizzo in modo efficiente (preoccupazioni: impegno, chiarezza, prestazioni)?

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

AGGIORNAMENTO

Ho ricevuto risposte su:

  • usando Criteri (ma usiamo principalmente HQL)
  • manipolazione della query String (ma tutti concordano sul fatto che sembra complicato e non molto sicuro)
  • racchiude la query , basandosi sull'ottimizzazione del database (ma si ha la sensazione che ciò non sia sicuro)

Speravo che qualcuno potesse dare opzioni lungo un altro percorso, più legato alla concatenazione di stringhe.
Potremmo creare entrambe le query HQL usando parti comuni ?

È stato utile?

Soluzione

Bella domanda. Ecco cosa ho fatto in passato (molte cose che hai già menzionato):

  1. Controlla se è presente la clausola SELEZIONA .
    1. In caso contrario, aggiungi select count (*)
    2. Altrimenti controlla se contiene DISTINCT o funzioni aggregate al suo interno. Se stai utilizzando ANTLR per analizzare la tua query, è possibile aggirare quelli ma è abbastanza coinvolto. Probabilmente stai meglio semplicemente racchiudendo il tutto con select count (*) da () .
  2. Rimuovi recupera tutte le proprietà
  3. Rimuovi fetch dai join se stai analizzando HQL come stringa. Se stai veramente analizzando la query con ANTLR puoi rimuovere completamente left join ; è piuttosto complicato controllare tutti i possibili riferimenti.
  4. Rimuovi ordina per
  5. A seconda di ciò che hai fatto in 1.2 dovrai rimuovere / modificare il gruppo in base a / avendo .

Quanto sopra si applica a HQL, naturalmente. Per le domande di Criteria sei piuttosto limitato a ciò che puoi fare perché non si presta facilmente alla manipolazione. Se stai usando una sorta di layer wrapper in cima a Criteri, finirai con l'equivalente di un sottoinsieme (limitato) di risultati dell'analisi ANTLR e in questo caso potresti applicare la maggior parte di quanto sopra.

Dato che normalmente tieni premuto per compensare la tua pagina corrente e il conteggio totale, di solito eseguo prima la query effettiva con il limite / offset dato ed eseguo la query count (*) solo se il numero di risultati restituiti è maggiore o uguale al limite E l'offset è zero (in tutti gli altri casi ho già eseguito il count (*) o ho comunque restituito tutti i risultati). Si tratta ovviamente di un approccio ottimistico per quanto riguarda le modifiche simultanee.

Aggiorna (sull'assemblaggio manuale di HQL)

Non mi piace particolarmente questo approccio. Se mappato come query denominata, HQL presenta il vantaggio del controllo degli errori di build-time (beh, tecnicamente runtime, poiché SessionFactory deve essere costruito sebbene ciò avvenga comunque durante i test di integrazione). Quando viene generato durante l'esecuzione, non riesce durante l'esecuzione :-) Anche l'ottimizzazione delle prestazioni non è esattamente facile.

Lo stesso ragionamento si applica ai Criteri, ovviamente, ma è un po 'più difficile sbagliare a causa di API ben definite rispetto alla concatenazione di stringhe. La creazione di due query HQL in parallelo (una di paging e "conteggio globale") porta anche alla duplicazione del codice (e potenzialmente più bug) o ti costringe a scrivere un qualche tipo di layer wrapper per farlo per te. Entrambe le strade sono tutt'altro che ideali. E se è necessario farlo dal codice client (come nell'API superiore), il problema peggiora ancora.

In realtà ho riflettuto parecchio su questo problema. L'API di ricerca da Hibernate-Generic-DAO sembra un compromesso ragionevole; ci sono maggiori dettagli nella mia risposta alla domanda collegata sopra.

Altri suggerimenti

Hai provato a chiarire le tue intenzioni a Hibernate impostando una proiezione sui tuoi criteri (SQL?)? Ho usato principalmente i criteri, quindi non sono sicuro di quanto applicabile al tuo caso, ma ho usato

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

e lasciando che Hibernate riesca a capire da solo la cache / il riutilizzo / le cose intelligenti .. Non sei sicuro di quante cose intelligenti faccia effettivamente però .. A qualcuno interessa commentare questo?

Beh, non sono sicuro che questa sia una buona pratica, ma è la mia pratica :)

Se ho come query qualcosa del tipo:

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 otterrò, eseguo:

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

E quindi ottenere il risultato come intero, senza mappare i risultati in un POJO.

Analizzare la tua query per rimuovere alcune parti, come 'ordina per' è molto complicato. Un buon RDBMS ottimizzerà la tua query per te.

Buona domanda.

In una situazione HQL a mano libera userei qualcosa del genere ma questo non è riutilizzabile in quanto è abbastanza specifico per le entità date

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

Fallo una volta e regola il numero iniziale di conseguenza fino alla pagina.

Per i criteri, anche se utilizzo 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 vale ogni volta chiamando ScrollableResults: last () .

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top