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

Dynamic HQL query using hql expressions rather than Criteria?

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

1
2
3
4
5
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而不是传递集合。我试图做到这一点,就像这样:

1
2
3
4
5
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的方法,而不是以任何方式动态地构建查询。

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

1
2
3
4
5
6
7
8
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);
}

查询如下:

1
2
3
4
5
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)


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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
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
3
4
5
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代码中执行以下操作:

1
2
3
4
5
session.getNamedQuery("your.query.name")
       .setParameterList("statusCodes", statusCodes)
       .setParameter("statusCodeCount", statusCodes.length)
       .setParameterList("orgIds", orgIds)
       .setParameter("orgIdCount", orgIds.length);

您需要确保数组为零长度而不是null,或者如果检查处理null情况则提供其他数组。

总而言之,HQL实际上更适合定义明确(例如静态)的查询。您可以解决动态参数,而不能解决动态排序。


您必须动态生成查询:

1
2
3
4
5
6
7
8
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");
}

当然,您还必须仅在参数不为null时将其设置为查询。