Come si fa una query limite in JPQL o HQL?


239

In Hibernate 3, c'è un modo per fare l'equivalente del seguente limite MySQL in HQL?

select * from a_table order by a_table_column desc limit 0, 20;

Non voglio usare setMaxResults se possibile. Ciò è stato sicuramente possibile nella versione precedente di Hibernate / HQL, ma sembra essere scomparso.


2
Sto usando Hibernate-5.0.12. Questo non è ancora disponibile? Sarebbe davvero pesante ottenere circa un milione di record e quindi applicare il filtro su di setMaxResultsesso, come notato da @Rachel nella risposta di @skaffman.
Rajeev Ranjan,

Risposte:


313

Questo è stato pubblicato sul forum di Hibernate alcuni anni fa quando gli è stato chiesto perché funzionasse in Hibernate 2 ma non in Hibernate 3:

Il limite non è mai stato una clausola supportata in HQL. Si intende utilizzare setMaxResults ().

Quindi, se ha funzionato in Hibernate 2, sembra che sia stato un caso, piuttosto che un design. Io penso ciò sia dovuto al fatto che il parser Hibernate 2 HQL avrebbe sostituito i bit della query che riconosceva come HQL e avrebbe lasciato il resto com'era, in modo da poter intrufolarsi in alcuni SQL nativi. Hibernate 3, tuttavia, ha un vero parser HQL AST ed è molto meno tollerante.

Penso che sia Query.setMaxResults()davvero la tua unica opzione.


3
Direi che l'approccio di Hibernate 3 è più corretto. Il tuo utilizzo di Hibernate è pensato per essere indipendente dal database, quindi dovresti fare questo tipo di cose in modo astratto.
matt b

6
Sono d'accordo, ma rende la migrazione un dolore reale nel culo quando le caratteristiche vengono lasciate cadere così.
Skaffman,

52
ma con setMaxResults, viene eseguita la prima query e quindi sul setMaxResultsset di risultati che si chiama che richiederebbe un numero limitato di righe di risultati dal set di risultati e lo visualizzerebbe per l'utente, nel mio caso ho 3 milioni di record che vengono interrogati e quindi sto chiamando setMaxResults per impostare 50 record, ma non voglio farlo, mentre eseguo una query, voglio eseguire una query per 50 record, esiste un modo per farlo?
Rachel,

2
Vecchio post lo so. Sono pienamente d'accordo con Rachel. Utilizzando NHibernate (porta .Net di Hibernate), ho recentemente aggiornato da 2 a 3 e la stessa cosa, la parte superiore X ora sta lanciando un errore parser. Tuttavia, quando ho aggiunto setMaxResults alla query, ha generato un TOP X nell'SQL risultante (utilizzando MsSql2008Dialect). Questo è buono.
Thierry_S,

17
@ Rachel Con setMaxResultsibernazione aggiungerà la limitparte alla query. Non otterrà tutti i risultati. Puoi controllare la query che produce abilitando:<property name="show_sql">true</property>
Andrejs il

148
 // SQL: SELECT * FROM table LIMIT start, maxRows;

Query q = session.createQuery("FROM table");
q.setFirstResult(start);
q.setMaxResults(maxRows);

6
Questo mi piace di più perché in setFirstResultrealtà è menzionato in questa risposta mentre qui e altrove dicono solo setMaxResultsquesto e setMaxResultsquello senza menzionare come impostare l'offset.
demongolem,

19

Se non si desidera utilizzare setMaxResults()l' Queryoggetto, è possibile tornare sempre all'utilizzo di SQL normale.


37
Non è poi così eccitante.
stevedbrown,

8
Non trovo HQL eccitante neanche. Perché non scrivere una vista sul tuo server DB che applica il limite e quindi ottenere HQL per guardare quella vista: P
pjp

1
È solo una di queste cose, mentre SQL è molto più semplice di HQL per ogni query, la creazione di viste e la scrittura di SQL nativi tende a non essere eccezionale per il refactoring. Cerco di evitarlo quando posso. Quel vero problema reale era che avevo scritto la mia query MySQL in modo errato e pensavo che setMaxResults fosse strano. Non lo era.
stevedbrown,

e se provi a passare da un fornitore DBMS all'altro, il dolore ti sta aspettando.
Olgun Kaya,

5

Se non vuoi usare setMaxResults, puoi anche usare Query.scroll invece dell'elenco e recuperare le righe che desideri. Utile ad esempio per il paging.


Grazie, la risposta accettata non ha risolto il problema per me, perché setMaxResults()carica prima ogni record in memoria e quindi crea un elenco secondario, che quando ci sono centinaia di migliaia o più record si arresta in modo anomalo sul server, perché è esaurito. Potrei tuttavia passare da una query tipizzata JPA a una query Hibernate QueryImpl hibernateQuery = query.unwrap(QueryImpl.class)e quindi potrei utilizzare il scroll()metodo come mi hai suggerito.
Toongeorges,

Almeno con il dialetto Oracle questo non è vero (Hibernate utilizza la colonna virtuale ROWNUM). Forse dipende dal conducente. Altri DB hanno la funzione TOP.
Lluis Martinez,

La mia query utilizza un recupero join. Ciò comporta l'avvertimento Hibernate "firstResult / maxResults specificato con il recupero della raccolta; applicazione in memoria". Quindi, con un recupero di join, Hibernate sta caricando l'intera raccolta in memoria. Eliminare il join non è un'opzione a causa di motivi di prestazioni. Quando uso ScrollableResults, ho più controllo su quali record vengono caricati in memoria. Non riesco a caricare tutti i record con un singolo ScrollableResults, perché anche questo porta a memoria insufficiente. Sto sperimentando il caricamento di più pagine con ScrollableResults diversi. Se questo non funziona, andrò con SQL.
Toongeorges,

È strano, non l'ho mai incontrato. Sì, a volte l'utilizzo diretto di JDBC è la strada da percorrere, specialmente per processi di massa / batch.
Lluis Martinez,

Le relazioni @OneToMany stanno causando i miei problemi. Se in qualche modo potessi eseguire la funzione di aggregazione Oracle LISTAGG in Hibernate per concatenare i valori multipli su un singolo valore, allora posso eliminare i join e sostituirli con una sottoquery.
Toongeorges,

2

Puoi facilmente usare l'impaginazione per questo.

    @QueryHints({ @QueryHint(name = "org.hibernate.cacheable", value = "true") })
    @Query("select * from a_table order by a_table_column desc")
    List<String> getStringValue(Pageable pageable);

devi passare new PageRequest(0, 1)per recuperare i record e dall'elenco recuperare il primo record.


2

Poiché questa è una domanda molto comune, ho scritto questo articolo , su cui si basa questa risposta.

I metodi setFirstResultesetMaxResults Query

Per un JPA e Hibernate Query, il setFirstResultmetodo è l'equivalente di OFFSETe il setMaxResultsmetodo è l'equivalente di LIMIT:

List<Post> posts = entityManager
.createQuery(
    "select p " +
    "from Post p " +
    "order by p.createdOn ")
.setFirstResult(10)
.setMaxResults(10)
.getResultList();

L' LimitHandlerastrazione

Hibernate LimitHandlerdefinisce la logica di impaginazione specifica del database e, come illustrato dal diagramma seguente, Hibernate supporta molte opzioni di impaginazione specifiche del database:

Implementazioni <code> LimitHandler </code>

Ora, a seconda del sistema di database relazionale sottostante che si sta utilizzando, la query JPQL precedente utilizzerà la sintassi di impaginazione corretta.

MySQL

SELECT p.id AS id1_0_,
       p.created_on AS created_2_0_,
       p.title AS title3_0_
FROM post p
ORDER BY p.created_on
LIMIT ?, ?

PostgreSQL

SELECT p.id AS id1_0_,
       p.created_on AS created_2_0_,
       p.title AS title3_0_
FROM post p
ORDER BY p.created_on
LIMIT ?
OFFSET ?

server SQL

SELECT p.id AS id1_0_,
       p.created_on AS created_on2_0_,
       p.title AS title3_0_
FROM post p
ORDER BY p.created_on
OFFSET ? ROWS 
FETCH NEXT ? ROWS ONLY

Oracolo

SELECT *
FROM (
    SELECT 
        row_.*, rownum rownum_
    FROM (
        SELECT 
            p.id AS id1_0_,
            p.created_on AS created_on2_0_,
            p.title AS title3_0_
        FROM post p
        ORDER BY p.created_on
    ) row_
    WHERE rownum <= ?
)
WHERE rownum_ > ?

Il vantaggio di usare setFirstResultesetMaxResults è che Hibernate può generare la sintassi di impaginazione specifica del database per qualsiasi database relazionale supportato.

E non sei limitato solo alle query JPQL. È possibile utilizzare il metodo setFirstResulte setMaxResultssette per le query SQL native.

Query SQL native

Non è necessario codificare in modo rigido l'impaginazione specifica del database quando si utilizzano query SQL native. Hibernate può aggiungerlo alle tue domande.

Quindi, se stai eseguendo questa query SQL su PostgreSQL:

List<Tuple> posts = entityManager
.createNativeQuery(
    "SELECT " +
    "   p.id AS id, " +
    "   p.title AS title " +
    "from post p " +
    "ORDER BY p.created_on", Tuple.class)
.setFirstResult(10)
.setMaxResults(10)
.getResultList();

Hibernate lo trasformerà come segue:

SELECT p.id AS id,
       p.title AS title
FROM post p
ORDER BY p.created_on
LIMIT ?
OFFSET ?

Bene, vero?

Oltre l'impaginazione basata su SQL

L'impaginazione è buona quando è possibile indicizzare i criteri di filtro e ordinamento. Se i requisiti di impaginazione implicano un filtro dinamico, è un approccio molto migliore l'utilizzo di una soluzione con indice invertito, come ElasticSearch.

Dai un'occhiata a questo articolo per maggiori dettagli.


1

String hql = "select userName from AccountInfo order by points desc 5";

Questo ha funzionato per me senza usare setmaxResults();

Fornisci il valore massimo nell'ultimo (in questo caso 5) senza usare la parola chiave limit. : P


7
Hm ... non sono troppo sicuro di questo, [citazione necessaria] questo potrebbe essere un incidente e potrebbe improvvisamente smettere di funzionare in una nuova versione di ibernazione.
Konrad 'ktoso' Malawski

4
Si prega di fare riferimento al documento ufficiale.
Fai Nhu Vy il

1
Questo non funziona per me in Hibernate Versione 5.2.12 Final
jDub9

1
Non funziona, non supporta anche l'ibernate 4.x.
Faiz Akram,

0

La mia osservazione è che anche se hai un limite in HQL (ibernazione 3.x), causerà un errore di analisi o semplicemente verrà ignorato. (se hai un ordine di + desc / asc prima del limite, verrà ignorato, se non hai desc / asc prima del limite, causerà un errore di analisi)


0

Se riesco a gestire un limite in questa modalità

public List<ExampleModel> listExampleModel() {
    return listExampleModel(null, null);
}

public List<ExampleModel> listExampleModel(Integer first, Integer count) {
    Query tmp = getSession().createQuery("from ExampleModel");

    if (first != null)
        tmp.setFirstResult(first);
    if (count != null)
        tmp.setMaxResults(count);

    return (List<ExampleModel>)tmp.list();
}

Questo è un codice davvero semplice per gestire un limite o un elenco.


0
Criteria criteria=curdSession.createCriteria(DTOCLASS.class).addOrder(Order.desc("feild_name"));
                criteria.setMaxResults(3);
                List<DTOCLASS> users = (List<DTOCLASS>) criteria.list();
for (DTOCLASS user : users) {
                System.out.println(user.getStart());
            }

0

È necessario scrivere una query nativa, fare riferimento a questo .

@Query(value =
    "SELECT * FROM user_metric UM WHERE UM.user_id = :userId AND UM.metric_id = :metricId LIMIT :limit", nativeQuery = true)
List<UserMetricValue> findTopNByUserIdAndMetricId(
    @Param("userId") String userId, @Param("metricId") Long metricId,
    @Param("limit") int limit);

0

È possibile utilizzare la query seguente

NativeQuery<Object[]> query = session.createNativeQuery(select * from employee limit ?)
query.setparameter(1,1);

0

Lo snippet di seguito viene utilizzato per eseguire query con limite utilizzando HQL.

Query query = session.createQuery("....");
query.setFirstResult(startPosition);
query.setMaxResults(maxRows);

È possibile ottenere l'applicazione demo a questo link .


-1
@Query(nativeQuery = true,
       value = "select from otp u where u.email =:email order by u.dateTime desc limit 1")
public List<otp> findOtp(@Param("email") String email);
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.