语法错误,Map<> 字段上的 QueryDSL “Case When”,带有聚合

Posted

技术标签:

【中文标题】语法错误,Map<> 字段上的 QueryDSL “Case When”,带有聚合【英文标题】:Syntax error, QueryDSL "Case When" on a Map<> field, with aggregation 【发布时间】:2016-03-28 16:16:20 【问题描述】:

我有一个event 表和一个eventData 表作为Map&lt;&gt; 与Hibernate 链接

事件 -> 地图 eventDatas

@OneToMany(fetch=FetchType.LAZY)
@JoinColumn(name = "event_id", referencedColumnName = "event_id")
@MapKey(name = "idk.key")
public Map<DATA_KEY, eventData> getEventDatas() 
    return eventDatas;

然后我有这个 QueryDSL 来获取按日期分组的 event 的聚合。

如果Map&lt;&gt; 包含一对DATA_KEY.CODE=202,则该组为"OK",否则,如果该对缺失或具有不同的值,则该组为"FAIL"

final QEvent event = QEvent.event;

Expression<String> groupCase = 
    event.eventDatas.get(DATA_KEY.CODE).valueInt 
    .when(202).then("OK") 
    .otherwise("FAIL");

ConstructorExpression<StatDto> constructor = 
    Projections.constructor(StatDto.class, event.date, event.count(), groupCase);

query.select(constructor) 
    .from(event) 
    .leftJoin(event.eventDatas) 
    .groupBy(event.date, groupCase) 
    .orderBy(event.date.asc());

return query.fetch();

此查询返回错误:

org.hibernate.hql.internal.ast.QuerySyntaxException: unexpected token: with near line 2, column 73 
[select event.date, count(event), case when event_eventDatas_0.valeurInt = ?1 then 'OK' else 'FAIL' end
from event.eventDatas as event_eventDatas_0 with key(event_eventDatas_0) = ?2, entitystat.event event
  left join event.eventDatas
group by event.date, case when event.eventDatas.get(?2).valeurInt = ?1 then 'OK' else 'FAIL' end
order by event.date]
    at org.hibernate.hql.internal.ast.QuerySyntaxException.convert(QuerySyntaxException.java:91)
    at org.hibernate.hql.internal.ast.ErrorCounter.throwQueryException(ErrorCounter.java:109)
    at org.hibernate.hql.internal.ast.QueryTranslatorImpl.parse(QueryTranslatorImpl.java:304)
    at org.hibernate.hql.internal.ast.QueryTranslatorImpl.doCompile(QueryTranslatorImpl.java:203)
    at org.hibernate.hql.internal.ast.QueryTranslatorImpl.compile(QueryTranslatorImpl.java:158)
    at org.hibernate.engine.query.spi.HQLQueryPlan.<init>(HQLQueryPlan.java:131)
    at org.hibernate.engine.query.spi.HQLQueryPlan.<init>(HQLQueryPlan.java:93)
    at org.hibernate.engine.query.spi.QueryPlanCache.getHQLQueryPlan(QueryPlanCache.java:167)
    at org.hibernate.internal.AbstractSessionImpl.getHQLQueryPlan(AbstractSessionImpl.java:301)
    at org.hibernate.internal.AbstractSessionImpl.createQuery(AbstractSessionImpl.java:236)
    at org.hibernate.internal.SessionImpl.createQuery(SessionImpl.java:1836)
    at com.querydsl.jpa.hibernate.DefaultSessionHolder.createQuery(DefaultSessionHolder.java:36)
    at com.querydsl.jpa.hibernate.AbstractHibernateQuery.createQuery(AbstractHibernateQuery.java:104)
    at com.querydsl.jpa.hibernate.AbstractHibernateQuery.createQuery(AbstractHibernateQuery.java:97)
    at com.querydsl.jpa.hibernate.AbstractHibernateQuery.fetch(AbstractHibernateQuery.java:174)

查询中没有groupCase,错误消失,证明问题出在案例本身。

在聚合中使用 Map&lt;&gt; 的正确语法是什么?

附录:

错误将with 标记指向from 行:

from event.eventDatas as event_eventDatas_0 with key(event_eventDatas_0) = ?2

这是 Hibernate 生成的没有 groupCase 的 SQL:

select event0_.date as col_0_0_, count(event0_.event_id) as col_1_0_ 
from event event0_ 
group by event0_.date
order by event0_.date

【问题讨论】:

能否提供不带groupCase的生成SQL?谢谢! @Bonifacio 我在问题末尾添加了 SQL。 问题解决了吗。 不,问题仍在继续。没有人找到正确的方法来正确地实现这一目标。 【参考方案1】:

在异常中,它显示了 sql 查询是如何被执行的。

选择 活动日期, 计数(事件), 当 event_eventDatas_0.valeurInt = ?1 然后 'OK' 否则 'FAIL' endfrom event.eventDatas as event_eventDatas_0 with key(event_eventDatas_0) = ?2, entitystat.event event 左加入 event.eventDatasgroup by event.date, case when event.eventDatas.get(?2).valeurInt = ?1 then 'OK' else 'FAIL' endorder by event.date

选择不应包含任何与 Projection refference 相关的表达式

上面的group by sql应该是group by event.date, alisaName

适合您的情况的 sql 语句:选择 活动日期, 计数(事件), event_eventDatas_0.valeurInt = ?1 then 'OK' else 'FAIL' end as aliasNamefrom event.eventDatas as event_eventDatas_0 with key(event_eventDatas_0) = ?2, entitystat.event event 左加入 event.eventDatasgroup by event.date, aliasNameorder by event.date

我没有测试下面的代码,但假设这应该可以修复它(添加别名是修复它的正确方法)。

final QEvent event = QEvent.event;
final QEventData eventData = QEventData.eventData;

String alias="alias";

Expression<String> groupCase = 
event.eventDatas.get(DATA_KEY.CODE).valueInt 
.when(202).then("OK") 
.otherwise("FAIL").as(alias);

ConstructorExpression<StatDto> constructor = 
Projections.constructor(StatDto.class, event.date, event.count(), groupCase);

query.select(constructor) 
.from(event) 
.leftJoin(event.eventDatas,eventData) 
.groupBy(event.date, alias) 
.orderBy(event.date.asc());

return query.fetch();

【讨论】:

感谢您的回答。我没有运气就根据您的示例测试了各种替代方案。错误仍然存​​在。 意外标记:在第 2 行第 77 列附近 [select DATE_FORMAT(event.datetime,'%Y-%m-%d %H:00'), count(event), (case when (event_eventData_0.valeurInt = ?1) then '2ok' else '1fail' end) as testAlias from event.eventData as event_eventData_0 with key(event_eventData_0) = ?2, event event left join event.eventData inner join event.logMouvement as m with m.logNoeudOrigine.idk.type = ?3 where event.type = ?4 and event.datetime between ?5 and ?6 group by DATE_FORMAT(event.datetime,'%Y-%m-%d %H:00') , testAlias order by event.datetime desc] 我认为问题出在from event.eventData as event_eventData_0 with key(event_eventData_0) = ?2, event event。这是自动添加的,但感觉不对。 我也有同感,应该不是event event,从帖子前面的例外来看应该是entitystat.event event left join event.eventDatas。你改变了你的query.select(constructor) .from(event) ............ 对,那是entitystat.event event。由于字数有限,我不得不缩小评论,而且我有点咄咄逼人。【参考方案2】:

阅读您的问题,我得出了与您相同的结论:

问题明显来自with,不知道怎么去掉

甚至可能无法使用 Hibernate,我最好的猜测是,当在查询的投影中使用 CASE WHEN 表达式时,它无法预测应该使用什么值来创建新对象。

您可以通过更改查询投影的构造函数以带来 EventData 键值,然后在 StatDto 构造函数中应用之前的 CASE WHEN 逻辑来解决此问题,如下所示:

首先,构造函数注解为@QueryProjection:

class StatDto 

  private Date eventDate;
  private int eventCount;
  private String eventDataStatus;

  @QueryProjection
  public StatDto(Date eventDate, int eventCount, int eventDataKey)
     this.eventDate = eventDate;
     this.eventCount = eventCount;
     if (eventDataKey == 202) 
        this.eventDataStatus = "OK";
      else 
        this.eventDataStatus = "FAIL";
     
  


查询投影构造函数:

query.from(event) 
    .leftJoin(event.eventDatas) 
    .groupBy(event.date, groupCase) 
    .orderBy(event.date.asc())
    .list(ConstructorExpression.create(StatDto.class, event.date, event.count(), event.eventDatas.get(DATA_KEY.CODE).valueInt));

注意:我不知道上述方法是否完全像这样工作,如果无论如何它不适用于显示的代码,请尝试使用给定的想法更改代码。

等待您的反馈,祝您好运!

【讨论】:

感谢您的回答。我试了一下。 @QueryProjection 很方便,但不能解决问题(我将从现在开始使用它)。可悲的是,如果我直接接收 dataKey 值,聚合将不正确,它将创建许多“失败”DTO,而不是只有一个带有总和。

以上是关于语法错误,Map<> 字段上的 QueryDSL “Case When”,带有聚合的主要内容,如果未能解决你的问题,请参考以下文章

错误 C2061:语法错误:标识符“地图”

<template /> 上的 Laravel Mix Vuejs 组件语法错误

25 mybatis-plus常用语法

带有 std::map 的模板函数给出错误:声明为 void 的变量或字段

Erlang,使用警卫之前的语法错误

GO语言struct语法