使用hql表达式而不是Criteria进行动态HQL查询?

时间:2012-05-17 17:43:05

标签: java hibernate 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的方法,而不是以任何方式动态构建查询。

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

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)

3 个答案:

答案 0 :(得分:5)

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

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));
}

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

答案 1 :(得分: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,或者在检查处理null方案时提供额外的数据。

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

答案 2 :(得分:0)

您必须动态生成查询:

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

当然,只有当参数不为空时,您还必须将参数设置为查询。