¿Cómo se hace una consulta de límite en JPQL o HQL?


239

En Hibernate 3, ¿hay alguna manera de hacer el equivalente del siguiente límite de MySQL en HQL?

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

No quiero usar setMaxResults si es posible. Esto definitivamente fue posible en la versión anterior de Hibernate / HQL, pero parece haber desaparecido.


2
Estoy usando Hibernate-5.0.12. ¿Todavía no está disponible? Sería realmente pesado obtener un millón de registros y luego aplicar el filtro setMaxResultssobre él, como notó @Rachel en la respuesta de @skaffman.
Rajeev Ranjan

Respuestas:


313

Esto fue publicado en el foro de Hibernate hace unos años cuando se le preguntó por qué esto funcionó en Hibernate 2 pero no en Hibernate 3:

El límite nunca fue una cláusula admitida en HQL. Debes usar setMaxResults ().

Entonces, si funcionó en Hibernate 2, parece que fue por coincidencia, en lugar de por diseño. Creo que esto se debió a que el analizador HQL de Hibernate 2 reemplazaría los bits de la consulta que reconoció como HQL, y dejaría el resto como estaba, para que pueda colarse en un SQL nativo. Hibernate 3, sin embargo, tiene un analizador AST HQL adecuado, y es mucho menos indulgente.

Creo que Query.setMaxResults()realmente es tu única opción.


3
Yo diría que el enfoque de Hibernate 3 es más correcto. Su uso de Hibernate está destinado a ser independiente de la base de datos, por lo que debería tener que hacer este tipo de cosas de manera abstracta.
mate b

66
Estoy de acuerdo, pero hace que la migración sea un verdadero dolor de cabeza cuando las características se eliminan así.
skaffman

52
pero con setMaxResults, primero se ejecuta la consulta y luego en el conjunto de resultados al que llama, setMaxResultslo que tomaría un número limitado de filas de resultados del conjunto de resultados y se lo mostraría al usuario, en mi caso tengo 3 millones de registros que se consultan y luego estoy llamando a setMaxResults para establecer 50 registros pero no quiero hacer eso, mientras que la consulta en sí quiero consultar 50 registros, ¿hay alguna manera de hacerlo?
Rachel

2
Publicación antigua lo sé. Estoy totalmente de acuerdo con Rachel. Usando NHibernate (.Net puerto de Hibernate), recientemente he actualizado de 2 a 3 y lo mismo, top X ahora arroja un error de analizador. Sin embargo, cuando agregué setMaxResults en la consulta, generó un TOP X en el SQL resultante (usando MsSql2008Dialect). Esto es bueno.
Thierry_S

17
@Rachel With setMaxResultshibernate agregará la limitparte a la consulta. No obtendrá todos los resultados. Puede verificar la consulta que produce habilitando:<property name="show_sql">true</property>
Andrejs

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

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

66
Este me gusta más porque en setFirstResultrealidad se menciona en esta respuesta, mientras que aquí y en otros lugares solo dicen setMaxResultsesto y setMaxResultsaquello sin mencionar cómo establecer el desplazamiento.
demongolem

19

Si no desea usar setMaxResults()el Queryobjeto, siempre puede volver a usar SQL normal.


37
Eso no es realmente tan emocionante.
stevedbrown

8
Tampoco me parece emocionante HQL. Por qué no escribir una vista en el servidor de base de datos que se aplica el límite y luego llegar a HQL vistazo a ese punto de vista: P
pjp

1
Es solo una de esas cosas, mientras que SQL es mucho más fácil que HQL para cada consulta, crear vistas y escribir SQL nativo tiende a no ser tan bueno para refactorizar. Intento evitarlo cuando puedo. Ese problema real real fue que escribí mal mi consulta MySQL de todos modos y pensé que setMaxResults era extraño. No lo fue.
stevedbrown

y si intenta cambiar entre diferentes proveedores de DBMS, el dolor lo está esperando.
Olgun Kaya el

5

Si no desea usar setMaxResults, también puede usar Query.scroll en lugar de list y buscar las filas que desee. Útil para paginación por ejemplo.


Gracias, la respuesta aceptada no resolvió el problema para mí, porque setMaxResults()primero carga cada registro en la memoria y luego crea una sublista, que cuando hay cientos de miles o más registros bloquea el servidor, porque no tiene memoria. Sin embargo, podría pasar de una consulta con tipo JPA a una consulta de Hibernate QueryImpl hibernateQuery = query.unwrap(QueryImpl.class)y luego podría usar el scroll()método como usted sugirió.
toongeorges

Al menos con el dialecto de Oracle esto no es cierto (Hibernate usa la columna virtual ROWNUM). Tal vez depende del conductor. Otros DB tienen la función TOP.
Lluis Martinez

Mi consulta está utilizando una búsqueda de unión. Esto da como resultado la advertencia de Hibernate "firstResult / maxResults especificada con la recuperación de la colección; se aplica en la memoria". Entonces, con una búsqueda de unión, Hibernate está cargando la colección completa en la memoria. Dejar caer la combinación no es una opción debido a razones de rendimiento. Cuando uso ScrollableResults, tengo más control sobre qué registros se cargan en la memoria. No puedo cargar todos los registros con un solo ScrollableResults, porque esto también se queda sin memoria. Estoy experimentando la carga de varias páginas con diferentes resultados de desplazamiento. Si esto no funciona, iré con SQL.
toongeorges

Eso es raro, nunca me he encontrado con eso. Sí, a veces usar JDBC directo es el camino a seguir, especialmente para procesos masivos / por lotes.
Lluis Martinez

Las relaciones de @OneToMany están causando mis problemas. Si de alguna manera pudiera ejecutar la función agregada de Oracle LISTAGG en Hibernate para concatenar los múltiples valores en un solo valor, entonces puedo soltar las uniones y reemplazarlas por una subconsulta.
toongeorges

2

Puede usar fácilmente la paginación para esto.

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

tienes que pasar new PageRequest(0, 1)para buscar registros y de la lista buscar el primer registro.


2

Como esta es una pregunta muy común, escribí este artículo , en el que se basa esta respuesta.

Los setFirstResulty setMaxResults Querymétodos

Para un JPA e Hibernate Query, el setFirstResultmétodo es el equivalente de OFFSET, y el setMaxResultsmétodo es el equivalente de LIMIT:

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

La LimitHandlerabstraccion

Hibernate LimitHandlerdefine la lógica de paginación específica de la base de datos y, como se ilustra en el siguiente diagrama, Hibernate admite muchas opciones de paginación específicas de la base de datos:

<code> LimitHandler </code> implementaciones

Ahora, dependiendo del sistema de base de datos relacional subyacente que esté utilizando, la consulta JPQL anterior utilizará la sintaxis de paginación adecuada.

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

La ventaja de usar setFirstResulty setMaxResultses que Hibernate puede generar la sintaxis de paginación específica de la base de datos para cualquier base de datos relacional compatible.

Y no está limitado únicamente a consultas JPQL. Puede usar el método setFirstResulty setMaxResultssiete para consultas SQL nativas.

Consultas SQL nativas

No tiene que codificar la paginación específica de la base de datos cuando utiliza consultas SQL nativas. Hibernate puede agregar eso a sus consultas.

Entonces, si está ejecutando esta consulta SQL en 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 transformará de la siguiente manera:

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

¿Guay, verdad?

Más allá de la paginación basada en SQL

La paginación es buena cuando puede indexar los criterios de filtrado y clasificación. Si sus requisitos de paginación implican un filtrado dinámico, es un enfoque mucho mejor utilizar una solución de índice invertido, como ElasticSearch.

Mira este artículo para más detalles.


1

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

Esto funcionó para mí sin usar setmaxResults();

Solo proporcione el valor máximo en el último (en este caso 5) sin usar la palabra clave limit. :PAGS


77
Hm ... no estoy muy seguro de esto, [cita requerida] esto puede ser un accidente y de repente podría dejar de funcionar en una nueva versión de hibernate.
Konrad 'ktoso' Malawski

44
Por favor, consulte el documento oficial.
Do Nhu Vy

1
Esto no funciona para mí en Hibernate Versión 5.2.12 Final
jDub9

1
No funciona, no admite hibernate 4.x también.
Faiz Akram

0

Mi observación es que incluso si tiene un límite en la HQL (hibernate 3.x), causará un error de análisis o simplemente se ignorará. (si tiene orden por + desc / asc antes del límite, se ignorará, si no tiene desc / asc antes del límite, causará un error de análisis)


0

Si puede administrar un límite en este 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 es un código realmente simple para manejar un límite o una lista.


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

Necesita escribir una consulta nativa, consulte esto .

@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

Puedes usar la consulta a continuación

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

0

El fragmento a continuación se utiliza para realizar consultas de límite utilizando HQL.

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

Puede obtener la aplicación de demostración en este enlace .


-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);
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.