如何根据特定顺序对休眠结果进行排序

Posted

技术标签:

【中文标题】如何根据特定顺序对休眠结果进行排序【英文标题】: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 这将是一个巨大的内存负载,除非您知道最佳解决方案 【参考方案1】:

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

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

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

    最后,把它们放在一起

    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

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

【讨论】:

以上是关于如何根据特定顺序对休眠结果进行排序的主要内容,如果未能解决你的问题,请参考以下文章

休眠条件排序依据

如何使用快速编码根据特定的排序顺序对自定义对象进行排序

如何使用注释在休眠中进行一对一映射

如何根据数组元素的顺序对查询结果进行排序?

如何使用休眠条件运行函数?

自定义订购 MSSQL 休眠 Spring Boot 2