2010-09-10 276 views
26

Hibernate與PostgreSQL DB一起使用,而按列排序desc會將null值置爲高於不爲空的值。Hibernate通過空值排序最後

SQL99標準提供關鍵字「NULLS LAST」來聲明空值應該低於非空值。

使用Hibernate的Criteria API可以實現「NULLS LAST」行爲嗎?

回答

14

鑑於HHH-465不是固定的,是不會得到固定在不久的將來由史蒂夫·埃伯索爾給出的理由,最好的選擇是使用連接到這個問題無論是全球範圍內CustomNullsFirstInterceptor或特別是改變SQL語句。

我下面張貼的讀者(學分埃米利奧·多爾切):

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

漂亮的解決方案,我沒有想過攔截器,謝謝!如果有人想使用它,你需要將這一行添加到你的persistence.xml文件中: mgamer 2010-09-10 12:13:47

+0

如果SQL在訂購後有限制/偏移,這會中斷 – Sathish 2011-04-01 14:52:47

+2

哇,Hibernate JIRA在** 2005中打開** – atrain 2011-07-26 15:10:06

2

這裏是我的更新由(帕斯卡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"); 
     } 
    } 
} 

這能解決問題:

這將打破如果sql有限制/偏移後排序-sathish 04年4月1日在14:52

而且,這裏是你如何可以在JPA(休眠)使用此:

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

我對Postgres的測試,這一點,它修復了「零點比非空高」,我們曾發行。

2

另一變型,如果動態地創建SQL,並且不使用標準的API:

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

此作品無論是VARCHAR或數字列。

+0

您確定,COALESCE由本地SQL中的Hibernate?Eben支持,我不確定所有的DMS都支持它嗎? – 2014-04-07 16:02:19

+0

我已經用PostgreSQL和Oracle對它進行了測試,並且,合併是Ansi SQL-92標準的一部分,所以它應該是 – 2014-04-17 20:59:36

34

如前所述,此功能在Hibernate 4.2.x和4.3.x版本中已經實現。

它可以用作例如:

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

休眠V4.3的javadocs不太遺漏的here

+1

這是新的正確答案 – MarcG 2016-04-13 20:00:11

+0

很高興我滾動下來,這種情況每次都會發生,最佳答案不是我們接受的答案 – 2017-05-08 12:15:58

2

您可以在休眠屬性中配置「nulls first」/「nulls last」,以便默認情況下通過任何標準調用獲取:hibernate.order_by.default_null_ordering=last(或=first)。詳情請參閱this hibernate commit

+0

感謝這個解決方案在我們的案例中比其他解決方案更清潔解決方案;系統將始終以相同的方式運行,而無需更改代碼。 – 2017-08-01 18:20:16