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); |
您需要确保数组为零长度而不是
总而言之,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时将其设置为查询。