2010-09-10 275 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