【问题标题】:Hibernate order by with nulls lastHibernate order by with nulls last
【发布时间】:2011-04-10 15:16:35
【问题描述】:

Hibernate 与 PostgreSQL DB 一起使用,同时按列对 desc 进行排序时,空值高于非空值。

SQL99 标准提供关键字“NULLS LAST”来声明空值应低于非空值。

可以使用 Hibernate 的 Criteria API 实现“NULLS LAST”行为吗?

【问题讨论】:

    标签: java hibernate null sql-order-by


    【解决方案1】:

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

    可以作为例子:

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

    Hibernate v4.3 javadocs 更少省略here

    【讨论】:

    • 很高兴我向下滚动。这种情况每次都更频繁地发生。最佳答案不是公认的答案。
    【解决方案2】:

    鉴于 HHH-465 尚未修复,并且由于 Steve Ebersole 给出的原因在不久的将来不会得到修复,您最好的选择是使用附加到问题的 CustomNullsFirstInterceptor 全局或专门用于更改 SQL 语句。

    我在下面为读者发布(感谢 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);
        }
    
    }
    

    【讨论】:

    • 很好的解决方案,我还没有考虑过拦截器,谢谢!如果其他人想使用它,您需要将此行添加到您的 persistence.xml 文件中:
    • 如果 sql 在 order by 后有限制/偏移量,则会中断
    • 哇,Hibernate JIRA 是在 2005
    • 似乎 HHH-465 已在 4.2 版(2013 年 3 月)中修复
    【解决方案3】:

    您可以在休眠属性中配置“nulls first”/“nulls last”,以便默认情况下任何条件调用都会拾取它:hibernate.order_by.default_null_ordering=last(或=first)。

    详情请见this hibernate commit

    【讨论】:

    • 感谢这个解决方案,在我们的案例中,它比其他解决方案更干净;系统将始终以相同的方式运行,无需更改代码。
    • 这真的很方便,因为还有使用 JPA Criteria API 设置 NullPrecedence 的选项
    • 感谢您的解决方案。如果有人想通过 spring boot + spring-data-jpa 实现,您可以在 application.properties spring.jpa.properties.hibernate.order_by.default_null_ordering=last 中使用以下配置
    【解决方案4】:

    这是(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");
            }
        }
    }
    

    这解决了问题:

    如果 sql 在 order by 后有限制/偏移量,这将中断 – Sathish 2011 年 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 上对此进行了测试,它解决了我们遇到的“空值高于非空值”问题。

    【讨论】:

      【解决方案5】:

      另一种变体,如果您动态创建 SQL 并且不使用 Criteria API:

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

      这适用于 varchar 或数字列。

      【讨论】:

      • 你确定 Hibernate 支持 COALESCE 吗?原生SQL中的Eben,不知道是不是所有的DMS都支持?
      • 我已经用 PostgreSQL 和 Oracle 测试过它。并且,coalesce 是 Ansi SQL-92 标准的一部分,因此应该得到所有供应商的支持。
      • 嗨,很抱歉 6 年后的通知,但我得到的是 NULL FIRST,还有其他选项可以最后得到 null 吗?
      【解决方案6】:

      我们可以使用以下 Sort 参数创建 Pageable 对象:

      JpaSort.unsafe(Sort.Direction.ASC, "ISNULL(column_name), (column_name)")
      

      我们也可以准备 HQL:

      String hql = "FROM EntityName e ORDER BY e.columnName NULLS LAST";
      

      【讨论】:

        【解决方案7】:

        对于未来的旅行者...我通过覆盖 Hibernate 方言解决了这个问题。在 CriteriaQuery 中默认情况下,我需要先为 asc 添加 null,最后为 desc 添加 null,由于某种原因不支持。 (旧版 CriteriaAPI 支持)

        package io.tolgee.dialects.postgres
        
        import org.hibernate.NullPrecedence
        import org.hibernate.dialect.PostgreSQL10Dialect
        
        @Suppress("unused")
        class CustomPostgreSQLDialect : PostgreSQL10Dialect() {
        
          override fun renderOrderByElement(expression: String?, collation: String?, order: String?, nulls: NullPrecedence?): String {
            if (nulls == NullPrecedence.NONE) {
              if (order == "asc") {
                return super.renderOrderByElement(expression, collation, order, NullPrecedence.FIRST)
              }
              if (order == "desc") {
                return super.renderOrderByElement(expression, collation, order, NullPrecedence.LAST)
              }
            }
            return super.renderOrderByElement(expression, collation, order, nulls)
          }
        }
        

        【讨论】:

          【解决方案8】:

          【讨论】:

          • 我已经下载了最新的 Hibernate 版本并且没有观察到任何行为变化。你?除此之外,工单的正式状态仍处于打开状态,未解决。
          猜你喜欢
          • 2016-12-30
          • 1970-01-01
          • 1970-01-01
          • 2013-04-24
          • 1970-01-01
          • 2019-01-03
          • 2013-10-11
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多