Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Dynamic HQL query using hql expressions rather than Criteria?

I am trying to write a partially dynamic HQL query without resorting to the Criteria API for various reasons. I wanted to know if there is an easy way to short circuit a where restriction using HQLs expressions. For example, here is the original query which works fine:

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

StatusCodes is a list of Strings and orgIds is a list of Integers. However, either one is optional and shouldn't restrict if null is passed instead of a collection. I've tried to accomplish this like so:

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)

This didn't work unfortunately, but is there any other approach that might work, either with using different expressions or passing in default values?

EDIT: Just to be clear I'm looking for a way to use a NamedQuery, not dynamically building the query in any way.

SOLUTION: I used the extra query parameters to accomplish it. I created two helper methods:

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

And the query like so:

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)
like image 873
ant-depalma Avatar asked May 17 '12 17:05

ant-depalma


1 Answers

My suggestion is to put all parameters in a map and build the query dynamic, after building before execution set all parameters required by the query taking values from the map:

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

Of course some improvements are needed for example throw exception when parameters is not set, use typed parameter if complexity is bigger than a few simple parameters and so on.

like image 88
Francisco Spaeth Avatar answered Sep 28 '22 15:09

Francisco Spaeth