引用条件查询中的早期别名字段

问题描述 投票:6回答:5

在此查询中:

CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery<Tuple> q = cb.createTupleQuery();

// FROM GamePlayedEvent gpe
Root<GamePlayedEvent> gpe = q.from(GamePlayedEvent.class);
// SELECT gameId, COUNT(*) AS count, AVG(duration)
// AS avDur, AVG(rewardCurrency) AS avCur, AVG(rewardXP) avXp
q.select(cb.tuple(
    gpe.<String>get("gameId"),
    cb.count(gpe).alias("count"),
    cb.avg(gpe.<Double>get("duration")).alias("avDur"),
    cb.avg(gpe.<Integer>get("rewardCurrency")).alias("avCur"),
    cb.avg(gpe.<Integer>get("rewardXp")).alias("avXp")
));
// WHERE loginTime BETWEEN ...
q.where(cb.between(gpe.<Date>get("time"), fromTime, toTime));
// GROUP BY gameId
q.groupBy(gpe.<String>get("gameId"));
// ORDER BY count DESC
q.orderBy(cb.desc(???));

如何添加ORDER BY count DESC,引用SELECT子句中定义的“count”?

java hibernate jpa criteria jpa-2.0
5个回答
7
投票

如果你刚刚捕获了count表达式并直接使用它会怎么样?

Expression event_count = cb.count(gpe);

q.select(cb.tuple( 
  gpe.<String>get("gameId"), 
  event_count,
  ... 
)); 

q.orderBy(cb.desc(event_count));

1
投票

尽管Pro JPA 2书中描述了别名方法可以用于生成sql查询别名(第251页),但我没有成功使其无法使用EclipseLink或Hibernate。对于你的问题,我会说你的orderBy行应该是:

q.orderBy(cb.desc(cb.count(gpe));

如果它得到了不同供应商的支持。

就我的研究而言,似乎别名方法仅用于命名选择中使用的管中的元素(因此仅用于投影)。

我有一个问题。为什么要对此查询使用JPA Criteria API。它(查询)接缝本质上是静态的,所以为什么不使用JPQL直接定义查询别名。


0
投票

您是否尝试使用别名设置投影?

criteria.setProjection(Projections.projectionList()
   .add(Projections.count("item.id"), "countItems"));
criteria.addOrder(Order.desc("countItems"));

0
投票

我今天遇到了同样的问题,但没有一个建议的解决方案对我有效,因为我不仅需要在order by子句中重用表达式,还需要在group by子句中重用该表达式。一个显而易见的解决方案是在数据库级别创建一个视图,但这有点笨拙,创建一个不必要的子查询,如果db用户没有被授予足够的权限,甚至不可能。我最终实现的更好的选择是写这样的东西

q.select(cb.tuple( 
  gpe.<String>get("gameId"), 
  cb.count(gpe),
  ... 
)).groupBy(cb.literal(2)).orderBy(cb.literal(2));

这种方法的第一个缺点是代码是错误的。另一个缺点是生成的sql查询包含序号位置表示法,它适用于某些数据库(如postgresql或mariadb),但不适用于其他数据库(如sql server)。然而,在我的情况下,我发现这是最好的选择。

在jpa 2.1上测试,hibernate 5.2.3作为提供者和postgresql 9.6。


0
投票

对于sum聚合字段,我有以下代码对我有用:

CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery<T> cq = cb.createQuery(entity);
Root<T> root = cq.from(entity);

cq.orderBy(cb.desc(cb.sum(root.get(orderByString))));

// orderByString is string entity field that is being aggregated and which we want to put in orderby clause as well. 
© www.soinside.com 2019 - 2024. All rights reserved.