使用 hql 表达式而不是条件的动态 HQL 查询?

新手上路,请多包涵

由于各种原因,我正在尝试编写一个部分动态的 HQL 查询而不求助于 Criteria API。我想知道是否有一种简单的方法可以使用 HQL 表达式来短路 where 限制。例如,这是工作正常的原始查询:

 SELECT customer
FROM Customer as customer
INNER JOIN customer.profile as profile
WHERE profile.status IN :statusCodes
AND   profile.orgId IN :orgIds

StatusCodes 是一个字符串列表,orgIds 是一个整数列表。但是,任何一个都是可选的,并且不应该限制是否传递 null 而不是集合。我试图像这样完成这个:

 SELECT customer
FROM Customer as customer
INNER JOIN customer.profile as profile
WHERE (:statusCodes IS NULL OR profile.status IN :statusCodes)
AND   (:orgIds IS NULL OR profile.orgId IN :orgIds)

不幸的是,这并没有奏效,但是有没有其他方法可以奏效,要么使用不同的表达式,要么传递默认值?

编辑:为了清楚起见,我正在寻找一种使用 NamedQuery 的方法,而不是以任何方式动态构建查询。

解决方案:我使用了额外的查询参数来完成它。我创建了两个辅助方法:

 private void setRequiredParameter(TypedQuery<?> query, String name, Object value) {
    query.setParameter(name, value);
}

private void setOptionalParameter(TypedQuery<?> query, String name, Object value) {
    query.setParameter(name, value);
    query.setParameter(name + "Optional", value == null ? 1 : 0);
}

查询是这样的:

 SELECT customer
        FROM Customer as customer
        INNER JOIN  customer.profile as profile
        WHERE (:statusCodesOptional = 1 OR profile.status IN :statusCodes)
        AND (:orgIdsOptional = 1 OR profile.orgId  IN :orgIds)

原文由 ant-depalma 发布,翻译遵循 CC BY-SA 4.0 许可协议

阅读 913
2 个回答

如果你绝对必须避免动态查询,你可以以两个附加参数为代价来做到这一点:

 SELECT customer
  FROM Customer AS customer
  JOIN customer.profile AS profile
 WHERE (profile.status IN :statusCodes OR :statusCodeCount = 0)
   AND (profile.orgId IN :orgIds OR :orgIdCount = 0)

在您的 Java 代码中,您将执行如下操作:

 session.getNamedQuery("your.query.name")
       .setParameterList("statusCodes", statusCodes)
       .setParameter("statusCodeCount", statusCodes.length)
       .setParameterList("orgIds", orgIds)
       .setParameter("orgIdCount", orgIds.length);

您需要确保数组的长度为零而不是 null 或提供额外的 if 检查来处理 null 场景。

综上所述,HQL 确实更适合定义明确(例如静态)的查询。您可以解决动态参数问题,但无法解决动态排序问题。

原文由 ChssPly76 发布,翻译遵循 CC BY-SA 3.0 许可协议

我的建议是将所有参数放在地图中并动态构建查询,在构建之后执行之前设置查询所需的所有参数从地图中获取值:

 Map<String, Object> pars = new HashMap<String,Object>();
pars.put("statusCodes", statusCodes);
pars.put("orgIds", orgIds);

StringBuilder b = "SELECT customer FROM Customer as customer INNER JOIN customer.profile as profile where 1 = 1";
if (statusCodes != null) {
  b.append(" and profile.status in :statusCodes");
}
if (orgIds != null) {
  b.append(" and profile.orgId in :statusCodes");
}

...

Query q = session.createQuery(b.toString());

...

for (String p : q.getNamedParameters()) {
  q.setParameter(p, pars.get(p));
}

当然需要进行一些改进,例如在未设置参数时抛出异常,如果复杂性大于几个简单参数则使用类型化参数等等。

原文由 Francisco Spaeth 发布,翻译遵循 CC BY-SA 3.0 许可协议

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题