Como você faz uma consulta de limite no JPQL ou HQL?

239

No Hibernate 3, existe uma maneira de fazer o equivalente ao seguinte limite do MySQL no HQL?

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

Não quero usar setMaxResults, se possível. Definitivamente, isso foi possível na versão mais antiga do Hibernate / HQL, mas parece ter desaparecido.

stevedbrown
fonte
2
Eu estou usando Hibernate-5.0.12. Isso ainda não está disponível? Seria muito pesado obter um milhão de registros e, em seguida, aplicar o filtro sobre ele setMaxResults, como observado por Rachel na resposta de @skaffman.
Rajeev Ranjan 23/10

Respostas:

313

Isso foi postado no fórum do Hibernate alguns anos atrás, quando perguntado sobre por que isso funcionou no Hibernate 2, mas não no Hibernate 3:

Limite nunca foi uma cláusula suportada no HQL. Você deve usar setMaxResults ().

Portanto, se funcionou no Hibernate 2, parece que foi por coincidência, e não por design. Eu acho que isso ocorreu porque o analisador Hibernate 2 HQL substituiria os bits da consulta que reconhecia como HQL e deixaria o restante como estava, para que você pudesse esgueirar-se em algum SQL nativo. O Hibernate 3, no entanto, possui um Analisador AST HQL adequado, e é muito menos tolerante.

Eu acho que Query.setMaxResults()realmente é sua única opção.

skaffman
fonte
3
Eu diria que a abordagem do Hibernate 3 é mais correta. Seu uso do Hibernate deve ser independente de banco de dados, portanto você deve fazer esse tipo de coisa de maneira abstrata.
Matt b
6
Eu concordo, mas faz com que a migração seja uma dor real quando os recursos são descartados dessa maneira.
22410 skaffman
52
mas com setMaxResults, a primeira consulta é executada e, em seguida, no conjunto de resultados que você chama, setMaxResultsque pegaria um número limitado de linhas de resultados do conjunto de resultados e exibia para o usuário; no meu caso, tenho 3 milhões de registros consultados e, em seguida, estou chamando setMaxResults para definir 50 registros, mas eu não quero fazer isso, enquanto a própria consulta eu quero consultar 50 registros, existe uma maneira de fazer isso?
Rachel
2
Post antigo que eu conheço. Eu concordo plenamente com Rachel. Usando o NHibernate (porta .Net do Hibernate), atualizei recentemente de 2 para 3 e a mesma coisa, o top X agora está lançando um erro no analisador. No entanto, quando adicionei setMaxResults na consulta, ele gerou um TOP X no SQL resultante (usando MsSql2008Dialect). Isso é bom.
Thierry_S 04/11
17
@ Rachel With setMaxResultshibernate acrescentará a limitparte à consulta. Não obterá todos os resultados. Você pode verificar a consulta que ela produz, ativando:<property name="show_sql">true</property>
Andrejs 23/12
148
 // SQL: SELECT * FROM table LIMIT start, maxRows;

Query q = session.createQuery("FROM table");
q.setFirstResult(start);
q.setMaxResults(maxRows);
Jessu
fonte
6
Eu gosto mais deste, porque setFirstResulté realmente mencionado nesta resposta, enquanto aqui e em outros lugares eles apenas dizem setMaxResultsisso e setMaxResultsaquilo sem mencionar como definir o deslocamento.
Demongolem
19

Se você não quiser usar setMaxResults()o Queryobjeto, poderá sempre voltar a usar o SQL normal.

pjp
fonte
37
Isso não é realmente tão emocionante.
stevedbrown
8
Também não acho o HQL interessante. Por que não escrever uma visão no seu servidor DB que se aplica o limite e, em seguida, obter HQL de olhar para esse ponto de vista: P
PJP
1
É apenas uma dessas coisas, enquanto o SQL é muito mais fácil que o HQL para cada consulta, criar visualizações e gravar SQL nativo tende a não ser tão bom para refatoração. Eu tento evitá-lo quando posso. O verdadeiro problema real foi que eu escrevi minha consulta MySQL de qualquer maneira errada e pensei que setMaxResults estava sendo estranho. Não foi.
stevedbrown
e se você tentar alternar entre diferentes fornecedores de DBMS, a dor estará esperando por você.
amigos estão
5

Se você não quiser usar o setMaxResults, também poderá usar o Query.scroll em vez da lista e buscar as linhas que desejar. Útil para paginação, por exemplo.

Lluis Martinez
fonte
Obrigado, a resposta aceita não resolveu o problema para mim, porque setMaxResults()carrega primeiro todos os registros na memória e depois cria uma sub-lista, que quando há centenas de milhares ou mais de registros trava o servidor, porque está sem memória. No entanto, eu poderia passar de uma consulta do tipo JPA para uma consulta do Hibernate QueryImpl hibernateQuery = query.unwrap(QueryImpl.class)e, em seguida, poderia usar o scroll()método conforme sugerido.
toongeorges
Pelo menos com o dialeto Oracle, isso não é verdade (o Hibernate usa a coluna virtual ROWNUM). Talvez isso dependa do motorista. Outros bancos de dados têm a função TOP.
Lluis Martinez
Minha consulta está usando uma busca de associação. Isso resulta no aviso de hibernação "firstResult / maxResults especificado com a busca de coleção; aplicação na memória". Portanto, com uma busca de junção, o Hibernate está carregando a coleção completa na memória. Descartar a junção não é uma opção por motivos de desempenho. Quando uso ScrollableResults, tenho mais controle sobre quais registros são carregados na memória. Não consigo carregar todos os registros com um único ScrollableResults, porque isso também resulta em falta de memória. Estou experimentando carregar várias páginas com diferentes ScrollableResults. Se isso não funcionar, irei com o SQL.
toongeorges
Estranho, eu nunca encontrei isso. Sim, às vezes, usar o JDBC direto é o caminho a seguir, especialmente para processos massivos / em lote.
Lluis Martinez
As relações @OneToMany estão causando meus problemas. Se, de alguma forma, eu pudesse executar a função agregada Oracle LISTAGG no Hibernate para concatenar os vários valores em um único valor, então eu posso descartar as junções e substituí-las por uma subconsulta.
toongeorges
2

Você pode facilmente usar a paginação para isso.

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

você precisa passar new PageRequest(0, 1)para buscar registros e da lista buscar o primeiro registro.

tk_
fonte
2

Como essa é uma pergunta muito comum, escrevi este artigo , no qual essa resposta se baseia.

Os métodos setFirstResultesetMaxResults Query

Para uma JPA e Hibernate Query, o setFirstResultmétodo é equivalente ae OFFSETo setMaxResultsmétodo é equivalente a LIMIT:

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

A LimitHandlerabstração

O Hibernate LimitHandlerdefine a lógica de paginação específica do banco de dados e, conforme ilustrado no diagrama a seguir, o Hibernate suporta muitas opções de paginação específicas do banco de dados:

Implementações <code> LimitHandler </code>

Agora, dependendo do sistema de banco de dados relacional subjacente que você estiver usando, a consulta JPQL acima usará a sintaxe de paginação apropriada.

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 ?

servidor 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

Oráculo

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_ > ?

A vantagem de usar setFirstResulte setMaxResultsé que o Hibernate pode gerar a sintaxe de paginação específica do banco de dados para qualquer banco de dados relacional suportado.

E você não está limitado apenas às consultas JPQL. Você pode usar o método setFirstResulte setMaxResultssete para consultas SQL nativas.

Consultas SQL nativas

Você não precisa codificar a paginação específica do banco de dados ao usar consultas SQL nativas. O Hibernate pode adicionar isso às suas consultas.

Portanto, se você estiver executando esta consulta SQL no 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();

O Hibernate irá transformá-lo da seguinte maneira:

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

Legal certo?

Além da paginação baseada em SQL

A paginação é boa quando você pode indexar os critérios de filtragem e classificação. Se seus requisitos de paginação implicam filtragem dinâmica, é uma abordagem muito melhor usar uma solução de índice invertido, como o ElasticSearch.

Confira este artigo para mais detalhes.

Vlad Mihalcea
fonte
1

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

Isso funcionou para mim sem usar setmaxResults();

Basta fornecer o valor máximo no último (neste caso, 5) sem usar a palavra-chave limit. : P

Dilawar
fonte
7
Hm ... não tenho muita certeza disso, [citação necessário] isso pode ser um acidente e de repente pode parar de funcionar em uma nova versão do hibernate.
Konrad 'ktoso' Malawski
4
Por favor recorra ao documento oficial.
Do Nhu Vy
1
Isso não está funcionando para mim na versão 5.2.12 do Hibernate Final
jDub9 29/11
1
Não está funcionando, não suporta o hibernate 4.x também.
Faiz Akram
0

Minha observação é que, mesmo que você tenha limite no HQL (hibernate 3.x), ele estará causando um erro de análise ou apenas será ignorado. (se você pedir por + desc / asc antes do limite, ele será ignorado; se você não tiver desc / asc antes do limite, isso causará erro de análise)

Xingsheng
fonte
0

Se pode gerenciar um limite neste modo

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();
}

Este é um código realmente simples para lidar com um limite ou uma lista.

biancardi
fonte
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());
            }
Deep Chand Jaipur
fonte
0

Você precisa escrever uma consulta nativa, consulte isso .

@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);
swatisinghi
fonte
0

Você pode usar a consulta abaixo

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

O trecho abaixo é usado para executar a consulta de limite usando o HQL.

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

Você pode obter o aplicativo de demonstração neste link .

Hari Krishna
fonte
-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);
Leena Varshney
fonte