【问题标题】:How to order result of hibernate based on a specific order如何根据特定顺序对休眠结果进行排序
【发布时间】:2015-09-25 16:25:34
【问题描述】:

我需要发送一个查询来检索具有特定字符组的值,如下所示:

假设我对“XX”感兴趣,因此它应该搜索其值以“XX”开头或具有“XX”(空格 XX)的任何字段。例如XXCDEFPD XXRFCMKJIEK XX 是有效结果。

我有以下查询 返回正确的结果,但我需要对它们进行排序 在某种程度上,它首先返回那些以 XX 开头的结果,然后返回其他结果。如下:

XXABCD
XXPLER
XXRFKF
AB XXAB
CD XXCD
ZZ XXOI
POLO XX

代码

Criteria criteria = session.createCriteria(Name.class, "name")
                .add(Restrictions.disjunction()
                     .add(Restrictions.ilike("name.fname", fname + "%"))
                     .add(Restrictions.ilike("name.fname", "%" + " " + fname + "%"))
                    )
                .setProjection(Projections.property("name.fname").as("fname"));
        List<String> names = (List<String>) criteria.list();

【问题讨论】:

  • 我不确定第 4 行到第 7 行的顺序是什么?
  • @Jegg 我应该显示以 XX 开头的字符串组。其余的(第 4 行到第 7 行)可以是中间有 XX(空格 XX)的那些,然后是末尾有 XX(空格 XX)的那些,另一种选择是在第 4 到第 7 行应用字母顺序。
  • 检索结果后为什么不在java中对它们进行排序?
  • @SeanF 这将是一个巨大的内存负载,除非您知道最佳解决方案

标签: java hibernate hibernate-criteria


【解决方案1】:

愚蠢,但它可能适用于您的情况。

既然你得到了正确的结果,你可以重建你的结果如下:

  1. 选取所有以 XX 开头的结果,然后将它们放入列表 L1 中,然后像 Collections.sort(L1) 一样进行正常排序;
  2. 对于所有其他结果,像 Collections.sort(L2)as list of L2 一样执行
  3. 最后,把它们放在一起

    List newList = new ArrayList(L1);

    newList.addAll(L2);

请注意。 Collections.sort 遵循其元素的自然顺序。

【讨论】:

  • 另一种选择是通过休眠检索单独的列表并将它们合并在一起,但我想知道是否可以通过单个查询来检索它们。
【解决方案2】:

Hibernate 支持订单:http://docs.jboss.org/hibernate/orm/4.2/devguide/en-US/html/ch11.html#ql-ordering 由于特殊标准,我认为您必须在 Hibernate 中自定义 Order。此链接可能会有所帮助: http://blog.tremend.ro/2008/06/10/how-to-order-by-a-custom-sql-formulaexpression-when-using-hibernate-criteria-api/

【讨论】:

    【解决方案3】:

    使用 JPQL (HQL)

    select fname from Name
    where upper(fname) like :fnameStart or upper(fname) like :fnameMiddle
    order by (case when upper(fname) like :fnameStart then 1 else 2 end), fname
    
    query.setParameter("fnameStart", "XX%");
    query.setParameter("fnameMiddle", "% XX%");
    

    有条件

    使用Criteria 会更棘手。首先,您必须在 order 子句中使用原生 SQL。其次,你必须绑定变量。

    public class FirstNameOrder extends Order {
        public FirstNameOrder() {
            super("", true);
        }
    
        @Override
        public String toSqlString(Criteria criteria, CriteriaQuery criteriaQuery) throws HibernateException {
            return "case when upper(FIRST_NAME) like ? then 1 else 2 end";
        }
    }
    

    case 表达式语法和upper 函数名应根据您的数据库进行更改(当然还有列名,如果不同的话)。

    很容易把这个加到Criteria,但是没有API来绑定参数。

    我试图通过将一个未使用的变量传递给自定义 sql 限制来欺骗 Hibernate,以便它有效地用于 order by 子句中的变量:

    Criteria criteria = session.createCriteria(Name.class, "name")
       .add(Restrictions.disjunction()
          .add(Restrictions.ilike("name.fname", fname + "%"))
          .add(Restrictions.ilike("name.fname", "%" + " " + fname + "%")))
       .setProjection(Projections.property("name.fname").as("fname"))
       .add(Restrictions.sqlRestriction("1 = 1", fname + "%", StringType.INSTANCE))
       .addOrder(new FirstNameOrder())
       .addOrder(Order.asc("fname"));
    

    而且效果很好。

    显然,不推荐使用此解决方案,我建议对此查询使用 JPQL。

    【讨论】:

    • 作为 OP 使用 Hibernate HQL 更合适。
    • 是的,但是查询应该是一样的。
    • @DraganBozanovic 感谢您的精彩回答,我查看了此链接mysqltutorial.org/mysql-case-statement 以更好地理解您的查询,但没有得到太多。你能告诉我查询号 1 和 2 指的是哪一部分吗?
    • 不客气。它们不引用查询的任何部分,它们只是数据库对行进行排序的常量表达式。由于排序是升序的(默认情况下),并且 1 小于 2,因此将 case 表达式评估为 1 的行排在将其评估为 2 的行之前。然后,使用第二个排序标准,我们对具有相同第一个标准的行进行排序值按fname 的字母顺序排列。
    • 非常好的技巧,真的救了我。我将参数的 HACK 直接放在 Order 类中;它之所以有效,是因为此时 WHERE 子句已被处理,因此您在最终查询中根本不会以 AND 1=1 结束,但该参数仍被添加到 WHERE 子句末尾附近。如果 GROUP BY 中有参数,仍然可能失败。
    【解决方案4】:

    运行两次选择,一次过滤所有以“XX”开头的字符串,第二次过滤其他字符串。

    【讨论】:

      【解决方案5】:

      您可以在条件中使用谓词...像这样:

      public List<Name> findByParameter(String key, String value, String orderKey)
      
                  CriteriaBuilder builder = this.entityManager.getCriteriaBuilder();
                  CriteriaQuery<Name> criteria = builder.createQuery(this.getClazz());
                  Root<Name> root = criteria.from(Name.getClass());
                  criteria.select(root);
                  List<Predicate> predicates = new ArrayList<Predicate>();
                  predicates.add(builder.equal(root.get(key), value));
                  criteria.where(predicates.toArray(new Predicate[predicates.size()]));
                  if (orderKey!=null  && !orderKey.isEmpty()) {
                      criteria.orderBy(builder.asc(root.get(orderKey)));
                  }
                  result = this.entityManager.createQuery(criteria).getResultList();
      
              return result;
      }
      

      【讨论】:

      • 您对 Dragan 的回答有何看法?
      【解决方案6】:

      如果你不想在内存中对结果进行排序,你可以修改你的条件,我会告诉你SQL

      select * from table where fname like 'XX%' order by fname
      union all
      select * from table where fname like '% XX%' order by fname
      union all
      select * from table where fname like '% XX' order by fname
      

      结果将是您的顺序和字母顺序,然后应用您的过滤器。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2020-06-18
        • 1970-01-01
        • 1970-01-01
        • 2015-11-06
        • 2011-04-03
        相关资源
        最近更新 更多