2010-09-10 12 views
26

Hibernación utilizada con PostgreSQL DB mientras ordena el desc de una columna coloca valores nulos mayores que los nulos.Orden de hibernación con nulos por última

El estándar SQL99 ofrece la palabra clave "NULLS LAST" para declarar que los valores nulos deben ponerse por debajo de los nulos.

¿Se puede lograr el comportamiento "NULLS LAST" utilizando la API de criterios de Hibernate?

Respuesta

14

Dado que HHH-465 no es fijo y no va a ser arreglados en un futuro próximo por las razones dadas por Steve Ebersole, su mejor opción sería utilizar el CustomNullsFirstInterceptor unido a la cuestión de forma global o específicamente para alterar la declaración de SQL.

he publicado a continuación para los lectores (créditos a 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<orderByNames.length; i++) { 
      if (orderByNames[i].trim().length() > 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); 
    } 

} 
+3

Solución bonita, no he pensado en interceptores, ¡gracias! En caso de que alguien más quiera usar esto, debe agregar esta línea a su archivo persistence.xml: mgamer

+0

Esto se rompe si sql tiene límite/desplazamiento después de orden por – Sathish

+2

Wow, que Hibernate JIRA se abrió en ** 2005 ** – atrain

2

Aquí está mi actualización de la clase (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"); 
     } 
    } 
} 

esto soluciona el problema :

Esto rompe si SQL tiene límite/desplazamiento después de la orden por - Sathish Abr 1 '11 a las 14:52

También aquí es cómo se puede utilizar esta dentro de la APP (hibernación):

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

He probado esto en postgres y se fija el 'nulos son más altos que los no nulos' tema que estábamos teniendo .

2

Otra variante, si crea SQL sobre la marcha y no utiliza los criterios del API:

ORDER BY COALESCE ('0') [ASC | DESC]

Esto funciona bien para varchar o columnas numéricas

+0

¿Está seguro , COALESCE es compatible con Hibernate? Eben en SQL nativo, no estoy seguro de que todo DMS lo soporte? –

+0

Lo probé con PostgreSQL y Oracle. Y, se unen es parte del estándar Ansi SQL-92, por lo que debería ser compatible con todos los proveedores. –

34

Esta característica se ha implementado durante las versiones de Hibernate 4.2.x y 4.3.x como se mencionó anteriormente.

Se puede utilizar como por ejemplo:

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

javadocs Hibernate V4.3 son menos omissive here.

+1

Esta es la nueva respuesta correcta. – MarcG

+0

Me alegro de poder desplazarme hacia abajo. Esto sucede cada vez más. La mejor respuesta no es la aceptada. –

2

Puede configurar "nulls first"/"nulls last" en las propiedades de hibernación, de modo que será recogido por cualquier llamada de criterio de manera predeterminada: hibernate.order_by.default_null_ordering=last (o =first).

Ver this hibernate commit para más detalles.

+0

Gracias por esta solución, en nuestro caso, era más limpia que otras soluciones, el sistema se comportará de la misma manera todo el tiempo sin hacer cambios de código. –