2010-09-10 12 views
26

Hibernate utilizzato con DB PostgreSQL mentre l'ordine di desc da una colonna mette valori nulli più alti di quelli non nulli.Sospendi l'ordine in base allo zero ultimo

SQL99 standard offre la parola chiave "NULLS LAST" per dichiarare che i valori nulli devono essere messi più in basso di null.

È possibile ottenere il comportamento "NULLS LAST" utilizzando l'API Criteri di Hibernate?

risposta

14

Dato che HHH-465 non è fisso e non ha intenzione di ottenere fisso in un prossimo futuro per i motivi indicati da Steve Ebersole, la soluzione migliore sarebbe quella di utilizzare il CustomNullsFirstInterceptor collegata al problema sia a livello globale o in particolare per modificare l'istruzione SQL.

sto postando qui sotto per i lettori (crediti 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

Bella soluzione, non ho pensato agli intercettori, grazie! Nel caso in cui qualcun altro voglia utilizzarlo, è necessario aggiungere questa riga al file persistence.xml: mgamer

+0

Questo rompe se sql ha limite/offset dopo ordine da – Sathish

+2

Wow, che Hibernate JIRA è stato aperto nel 2005 ** ** – atrain

2

Ecco il mio aggiornamento alla classe (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"); 
     } 
    } 
} 

questo risolve il problema :

Questo rompe se sql ha limite/offset dopo ordine da - Sathish 1 aprile '11 a 14:52

qui anche come è possibile utilizzare questo all'interno 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); 
    } 
} 

Ho provato questo su Postgres e fissa il 'nulli sono più alti rispetto ai non-valori null' problema che stavamo avendo .

2

Un'altra variante, se si crea SQL al volo e non si utilizza Criteri API:

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

Questo funziona sia per varchar o colonne numeriche.

+0

Sei sicuro , COALESCE è supportato da Hibernate? Eben in SQL nativo, non sono sicuro che tutti i DMS lo supportano? –

+0

L'ho testato con PostgreSQL e Oracle e la coalesce fa parte dello standard ANSI SQL-92, quindi dovrebbe essere supportato da tutti i fornitori –

34

Questa funzione è stata implementata durante le versioni di Hibernate 4.2.xe 4.3.x come precedentemente menzionato.

Può essere utilizzata come ad esempio:

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

javadocs Sospensione v4.3 sono meno omissiva here.

+1

Questa è la nuova risposta corretta – MarcG

+0

Sono contento di averlo fatto scorrere verso il basso.Questo accade più spesso ogni volta.La migliore risposta non è la risposta accettata –

2

È possibile configurare "nulls first"/"nulls last" nelle proprietà di ibernazione in modo che venga prelevato da qualsiasi criterio di chiamata per impostazione predefinita: hibernate.order_by.default_null_ordering=last (o =first).

Vedere this hibernate commit per dettagli.

+0

Grazie per questa soluzione, nel nostro caso, era più pulito di altri soluzioni: il sistema si comporterà sempre nello stesso modo senza apportare modifiche al codice. –

Problemi correlati