如何根据特定顺序对休眠结果进行排序
Posted
技术标签:
【中文标题】如何根据特定顺序对休眠结果进行排序【英文标题】:How to order result of hibernate based on a specific order 【发布时间】:2015-09-25 16:25:34 【问题描述】:我需要发送一个查询来检索具有特定字符组的值,如下所示:
假设我对“XX”感兴趣,因此它应该搜索其值以“XX”开头或具有“XX”(空格 XX)的任何字段。例如XXCDEF
、PD XXRF
和CMKJIEK 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
结果将是您的顺序和字母顺序,然后应用您的过滤器。
【讨论】:
以上是关于如何根据特定顺序对休眠结果进行排序的主要内容,如果未能解决你的问题,请参考以下文章