1. Información general
Este artículo ilustra cómo ordenar con Hibernate , utilizando tanto Hibernate Query Language (HQL) como la API Criteria.
2. Clasificación con HQL
Ordenar con HQL de Hibernate es tan simple como agregar la cláusula Order By a la cadena de consulta HQL:
String hql = "FROM Foo f ORDER BY f.name"; Query query = sess.createQuery(hql);
Después de ejecutar este código, Hibernate generará la siguiente consulta SQL:
Hibernate: select foo0_.ID as ID1_0_, foo0_.NAME as NAME2_0_ from FOO foo0_ order by foo0_.NAME
La dirección del orden de clasificación predeterminado es ascendente. Es por eso que la condición de pedido, asc , no se incluye en la consulta SQL generada.
2.1. Usar un orden de clasificación explícito
Para especificar el orden de clasificación manualmente, deberá incluir la dirección del pedido en la cadena de consulta HQL :
String hql = "FROM Foo f ORDER BY f.name ASC"; Query query = sess.createQuery(hql);
En este ejemplo, la configuración de la cláusula asc en el HQL se incluyó en la consulta SQL generada:
Hibernate: select foo0_.ID as ID1_0_, foo0_.NAME as NAME2_0_ from FOO foo0_ order by foo0_.NAME ASC
2.2. Ordenar por más de un atributo
Se pueden agregar varios atributos, junto con un orden de clasificación opcional, a la cláusula Order By en la cadena de consulta HQL:
String hql = "FROM Foo f ORDER BY f.name DESC, f.id ASC"; Query query = sess.createQuery(hql);
La consulta SQL generada cambiará en consecuencia:
Hibernate: select foo0_.ID as ID1_0_, foo0_.NAME as NAME2_0_ from FOO foo0_ order by foo0_.NAME DESC, foo0_.ID ASC
2.3. Establecer la prioridad de clasificación de valores nulos
De forma predeterminada, cuando el atributo por el que se debe ordenar tiene valores nulos , el RDMS decide la precedencia. Este tratamiento predeterminado se puede anular colocando una cláusula NULLS FIRST o NULLS LAST en la cadena de consulta HQL .
Este simple ejemplo coloca cualquier nulo al final de la lista de resultados:
String hql = "FROM Foo f ORDER BY f.name NULLS LAST"; Query query = sess.createQuery(hql);
Veamos la cláusula es nula y luego 1 else 0 en la consulta SQL generada :
Hibernate: select foo0_.ID as ID1_1_, foo0_.NAME as NAME2_1_, foo0_.BAR_ID as BAR_ID3_1_, foo0_.idx as idx4_1_ from FOO foo0_ order by case when foo0_.NAME is null then 1 else 0 end, foo0_.NAME
2.4. Clasificación de relaciones de una a varias
Analicemos un caso de ordenación complejo: ordenar entidades en una relación de uno a muchos - Barra que contiene una colección de entidades Foo .
Haremos esto anotando la colección con la anotación Hibernate @OrderBy ; especificaremos el campo por el que se realiza el pedido, así como la dirección:
@OrderBy(clause = "NAME DESC") Set fooList = new HashSet();
Observe el argumento de la cláusula de la anotación. Esto es exclusivo de @OrderBy de Hibernate , en comparación con una anotación similar de @OrderBy JPA. Otra característica que diferencia este enfoque de su equivalente JPA es que el argumento de la cláusula indica que la clasificación se realiza en función de la columna NAME de la tabla FOO , no en el atributo de nombre de Foo .
Ahora veamos la clasificación real de Bars y Foos :
String hql = "FROM Bar b ORDER BY b.id"; Query query = sess.createQuery(hql);
La declaración SQL resultante muestra que los Foo ordenados se colocan en una fooList:
Hibernate: select bar0_.ID as ID1_0_, bar0_.NAME as NAME2_0_ from BAR bar0_ order by bar0_.ID Hibernate: select foolist0_.BAR_ID as BAR_ID3_0_0_, foolist0_.ID as ID1_1_0_, foolist0_.ID as ID1_1_1_, foolist0_.NAME as NAME2_1_1_, foolist0_.BAR_ID as BAR_ID3_1_1_, foolist0_.idx as idx4_1_1_ from FOO foolist0_ where foolist0_.BAR_ID=? order by foolist0_.NAME desc
Una cosa a tener en cuenta es que no es posible ordenar Listas como fue el caso con JPA. La documentación de Hibernate dice:
“Hibernate actualmente ignora @OrderBy en @ElementCollection en, por ejemplo, List. El orden de los elementos es el que devuelve la base de datos, indefinido ".
Como nota al margen, sería posible evitar esta limitación utilizando la configuración XML heredada para Hibernate y reemplazando la elemento con un elemento.
3. Clasificación con criterios de hibernación
Criteria Object API proporciona la clase Order como API principal para gestionar la clasificación.
3.1. Establecer el orden de clasificación
La clase Order tiene dos métodos para establecer el orden de clasificación:
- asc (atributo de cadena) : ordena la consulta por atributo en orden ascendente.
- desc (atributo de cadena) : ordena la consulta por atributo en orden descendente.
Comencemos con un ejemplo simple: ordenar por un solo atributo de identificación :
Criteria criteria = sess.createCriteria(Foo.class, "FOO"); criteria.addOrder(Order.asc("id"));
Tenga en cuenta que el argumento del método asc distingue entre mayúsculas y minúsculas y debe coincidir con el nombre del atributo por el que se debe ordenar.
La API de objetos de Hibernate Criteria establece explícitamente una dirección de orden de clasificación y esto se refleja en la declaración SQL generada por el código:
Hibernate: select this_.ID as ID1_0_0_, this_.NAME as NAME2_0_0_ from FOO this_ order by this_.ID sac
3.2. Ordenar por más de un atributo
Ordenar por múltiples atributos solo requiere agregar un objeto Order a la instancia de Criteria , como en el siguiente ejemplo:
Criteria criteria = sess.createCriteria(Foo.class, "FOO"); criteria.addOrder(Order.asc("name")); criteria.addOrder(Order.asc("id"));
La consulta que se genera en SQL es:
Hibernate: select this_.ID as ID1_0_0_, this_.NAME as NAME2_0_0_ from FOO this_ order by this_.NAME asc, this_.ID sac
3.3. Establecer la prioridad de clasificación de valores nulos
De forma predeterminada, cuando el atributo por el que se debe ordenar tiene valores nulos , el RDMS decide la precedencia. La API de objetos de criterios de Hibernate simplifica el cambio de ese valor predeterminado y coloca nulos al final de una lista ordenada ascendente:
Criteria criteria = sess.createCriteria(Foo.class, "FOO"); criteria.addOrder(Order.asc("name").nulls(NullPrecedence.LAST));
Aquí está la consulta SQL subyacente , con la cláusula es nula y luego 1 más 0 :
Hibernate: select this_.ID as ID1_1_1_, this_.NAME as NAME2_1_1_, this_.BAR_ID as BAR_ID3_1_1_, this_.idx as idx4_1_1_, bar2_.ID as ID1_0_0_, bar2_.NAME as NAME2_0_0_ from FOO order by case when this_.NAME is null then 1 else 0 end, this_.NAME asc
Alternativamente, también podemos colocar los nulos al comienzo de una lista ordenada descendente:
Criteria criteria = sess.createCriteria(Foo.class, "FOO"); criteria.addOrder(Order.desc("name").nulls(NullPrecedence.FIRST));
A continuación, se muestra la consulta SQL correspondiente, con la cláusula es nula y luego 0 más 1 :
Hibernate: select this_.ID as ID1_1_1_, this_.NAME as NAME2_1_1_, this_.BAR_ID as BAR_ID3_1_1_, this_.idx as idx4_1_1_, bar2_.ID as ID1_0_0_, bar2_.NAME as NAME2_0_0_ from FOO order by case when this_.NAME is null then 0 else 1 end, this_.NAME desc
Tenga en cuenta que, si el atributo para ordenar por un tipo primitivo como un int, un PresisitenceException será lanzada .
Por ejemplo, si el valor de f.anIntVariable es nulo, la ejecución de la consulta:
String jql = "Select f from Foo as f order by f.anIntVariable desc NULLS FIRST"; Query sortQuery = entityManager.createQuery(jql);
arrojará:
javax.persistence.PersistenceException: org.hibernate.PropertyAccessException: Null value was assigned to a property of primitive type setter of com.cc.jpa.example.Foo.anIntVariable
4. Conclusión
Este artículo explora la ordenación con Hibernate, utilizando las API disponibles para entidades simples y también para entidades en una relación de uno a muchos.
La implementación de este Tutorial de clasificación de Hibernate se puede encontrar en el proyecto github; este es un proyecto basado en Eclipse, por lo que debería ser fácil de importar y ejecutar tal como está.