Ordem de hibernação com nulos por último

O Hibernate usado com o PostgreSQL DB enquanto ordena desc por uma coluna coloca valores nulos maiores que os nulos.

O padrão SQL99 oferece a palavra-chave “NULLS LAST” para declarar que valores nulos devem ser colocados abaixo de nulos.

O comportamento “NULLS LAST” pode ser alcançado usando a API Criteria do Hibernate?

Dado que o HHH-465 não é fixo e não será consertado em um futuro próximo pelas razões dadas por Steve Ebersole, sua melhor opção seria usar o CustomNullsFirstInterceptor anexado ao problema globalmente ou especificamente para alterar a instrução SQL.

Estou postando abaixo para os leitores (créditos para Emilio Dolce):

 public class CustomNullsFirstInterceptor extends EmptyInterceptor { private static final long serialVersionUID = -3156853534261313031L; private static final String ORDER_BY_TOKEN = "order by"; public String onPrepareStatement(String sql) { int orderByStart = sql.toLowerCase().indexOf(ORDER_BY_TOKEN); if (orderByStart == -1) { return super.onPrepareStatement(sql); } orderByStart += ORDER_BY_TOKEN.length() + 1; int orderByEnd = sql.indexOf(")", orderByStart); if (orderByEnd == -1) { orderByEnd = sql.indexOf(" UNION ", orderByStart); if (orderByEnd == -1) { orderByEnd = sql.length(); } } String orderByContent = sql.substring(orderByStart, orderByEnd); String[] orderByNames = orderByContent.split("\\,"); for (int i=0; i 0) { if (orderByNames[i].trim().toLowerCase().endsWith("desc")) { orderByNames[i] += " NULLS LAST"; } else { orderByNames[i] += " NULLS FIRST"; } } } orderByContent = StringUtils.join(orderByNames, ","); sql = sql.substring(0, orderByStart) + orderByContent + sql.substring(orderByEnd); return super.onPrepareStatement(sql); } } 

Este recurso foi implementado durante as versões do Hibernate 4.2.xe 4.3.x, conforme mencionado anteriormente.

Pode ser usado como por exemplo:

 Criteria criteria = ...; criteria.addOrder( Order.desc( "name" ).nulls(NullPrecedence.FIRST) ); 

Os javadocs do Hibernate v4.3 são menos omissivos aqui .

Você pode configurar “nulls first” / “nulls last” nas propriedades de hibernação, de modo que ele será selecionado por qualquer critério chamado por padrão: hibernate.order_by.default_null_ordering=last (ou =first ).

Veja este commit do hibernate para detalhes.

Aqui está minha atualização para a class por (Pascal Thivent):

 for (int i = 0; i < orderByNames.length; i++) { if (orderByNames[i].trim().length() > 0) { String orderName = orderByNames[i].trim().toLowerCase(); if (orderName.contains("desc")) { orderByNames[i] = orderName.replace("desc", "desc NULLS LAST"); } else { orderByNames[i] = orderName.replace("asc", "asc NULLS FIRST"); } } } 

Isso corrige o problema:

Isso quebra se sql tem limite / deslocamento após a ordem por – Sathish Abr 1 ’11 às 14:52

Veja também como você pode usar isso dentro do JPA (hibernate):

 Session session = entityManager.unwrap(Session.class); Session nullsSortingProperlySession = null; try { // perform a query guaranteeing that nulls will sort last nullsSortingProperlySession = session.getSessionFactory().withOptions() .interceptor(new GuaranteeNullsFirstInterceptor()) .openSession(); } finally { // release the session, or the db connections will spiral try { if (nullsSortingProperlySession != null) { nullsSortingProperlySession.close(); } } catch (Exception e) { logger.error("Error closing session", e); } } 

Eu testei isso no postgres e ele corrige o problema de ‘nulos são mais altos que os não-nulos’ que estávamos tendo.

Outra variante, se você criar SQL rapidamente e não usar a API Criteria:

ORDER BY COALESCE (, ‘0’) [ASC | DESC]

Isso funciona para colunas varchar ou numéricas.

Parece haver um pedido de alteração / ticket de bug aberto para este