1

I want to pass two parameters to namedquery. One is number type and the other is String type. They both could be null.

For instance, (id=null, username='joe') and (id=1, username='joe') are two different results. In namedQuery, the syntax is "u.id is null" if id is null, but "u.id = :id" if id is not null. My question is how to dynamically handle the id filed in namedQuery?

Please check my sample code:

1.User.java

@NamedQueries({
        @NamedQuery(name = "getUser", query = "select u from User u"
                + " where u.id = :id"    
                + " And u.username= :username")
})
public class User{
     public Long id;
     public String username;
}
  1. UserDao.java
public User getUser(Long id, String username) {
    TypedQuery<User> query = Dao.entityManager.createNamedQuery("getUser", User.class);
    query.setParameter("id", id);
    query.setParameter("username", username);

    List<User> users = query.getResultList();

    if (users.size() > 0) {
        return users.get(0);
    } else {
        return null;
    }
}

=======================================

What I have tried:

  1. This is legacy code and I don't want to change the structure. So I don't want to use Criteria.

  2. select u from User u where (:id is null or u.id= :id) and u.username= :username

    // throw exception: inconsistent datatypes: expected NUMBER got BINARY

  3. select u from User u where u.id= nullif(:id, null) and u.username= :username

    // Throw exception: inconsistent datatypes: expected NUMBER got BINARY

  4. I also tried nvl and decode in namedQuery, didn't work.

  5. query.setParameter("id", id==null?-1:id) // didn't work.

My last choice will be writing query in UserDao file to replace namedQuery in User file.

Thank you !

===========================================

I am running out of time and have to give up using namedQuery. My solution:

# UserDao.java 

  public User getUser(Long id, String usename) {
        String getUser = "select u from user u where u.id " + Dao.isNull(id) 
                       + " And u.username " + Dao.isNull(username);
        Query query = Dao.entityManager.createQuery(getUser);
    }

# Dao.java

   public static String isNull(Object field) {
        if (field != null) {
                if (field instanceof String) {
                    return " = " + "'" + field + "'";
                } else {
                    return " = " + field;
                }

            } else {
                return " is NULL ";
            }
    }
Bryan
  • 400
  • 1
  • 3
  • 15
  • Are you sure the problem is with parameters? Beside this null issues, the query works and returns proper results? – Predrag Maric Oct 30 '14 at 15:44
  • Doesn't oracle treat empty strings as Null anyway? – zmf Oct 30 '14 at 15:45
  • Yes. This is null issues. The code worked fine before. I just got this issue because we want to change the requirement that id field could be null. Previously, the id field is non-null. – Bryan Oct 30 '14 at 15:49
  • Is that id field the primary key? Why would you want a primary key field that accepts null values? Sounds wrong to me! – Ben Thurley Oct 30 '14 at 17:16
  • Going further if it is a unique primary key then the second part of that query is irrelevant. – Ben Thurley Oct 30 '14 at 17:20
  • @BenThurley id field is not the primary key. Maybe this is not a good example. If the id bothers you, you may consider it as age. What I want to mention is number type value. – Bryan Oct 30 '14 at 17:22
  • @BenThurley In a table, the primary key is usually automatically generated. So the primary key is rarely used to fetch data. – Bryan Oct 30 '14 at 17:27
  • "So the primary key is rarely used to fetch data" - Not sure I agree with that! – Ben Thurley Oct 30 '14 at 17:29
  • May be useful http://stackoverflow.com/questions/8643564/how-can-i-query-a-null-in-a-long-value-without-getting-expected-number-but-got – Ben Thurley Oct 30 '14 at 18:02

3 Answers3

4

You cannot change the named query at run time. Doing so would defeat the purpose of the named query. Dynamic queries should be created using the criteria api.

See this answer on what to use in the named query.

   from CountryDTO c where 
    ((:status is null and c.status is null) or c.status = :status) 
    and c.type =:type
Community
  • 1
  • 1
zmf
  • 9,095
  • 2
  • 26
  • 28
1

Try to let a boolean check the null. It worked for me.

query.setParameter("idIsSet", id != null); 

query.setParameter("id", id); 

In the named query:

(:idIsSet = false or id = :id)

When id is null, the "idIsSet" is false. So ":idIsSet = false" turns into true and the id will not be checked.

Matt
  • 1,518
  • 4
  • 16
  • 30
minibi
  • 50
  • 7
0

If ) at the end of your query was a mistake, try without it

    @NamedQuery(name = "getUser", query = "select u from User u"
            + " where (:id is null or u.id = :id)"    
            + " And :username"

EDIT

If everything else fails, this quick workaround should do it (if you don't have negative ids in database) in combination with the above null check

query.setParameter("id", id == null ? -1 : id); 
Predrag Maric
  • 23,938
  • 5
  • 52
  • 68
  • Thanks for your correction. That was my typo. The method didn't work. – Bryan Oct 30 '14 at 15:34
  • This is a query, not a java statement. `:id is null` is meaningless in the query world, because `:id` is the variable is passed which is unknown to database. – pms Oct 30 '14 at 15:34
  • @pmp It's not meaningless, it uses the variable on purpose. The point of expressions like this is, if you pass `null` as a param value, left side of the `OR` will be evaluated to `true`, so right side won't be considered at all. – Predrag Maric Oct 30 '14 at 15:39
  • @Bryan [This](http://stackoverflow.com/a/8645512/4074715) post suggests a workaround like this `WHERE :id is null OR u.id = COALESCE(:id, -1)`, worth a try. – Predrag Maric Oct 30 '14 at 15:54
  • @PredragMaric Can you show me any tutorial that explain this "OR" operator? Thank you ! – Bryan Oct 30 '14 at 15:57
  • @Bryan Nothing special about it, you have ` OR `, which evaluates to `true` or `false`. The trick here is that if `` is true, then the whole expression is true and right side doesn't need to be evaluated. Java works the same way. – Predrag Maric Oct 30 '14 at 16:04
  • @Bryan Did you try `COALESCE` suggestion? – Predrag Maric Oct 30 '14 at 16:05
  • @PredragMaric I just tried COALESCE. Throw the same exception. I understand the OR operator in java. My curious is how does this "OR" work in namedQuery. What if I want to express the sql "OR" in namedQuery? – Bryan Oct 30 '14 at 16:12
  • @Bryan There is nothing specific with `OR` in named queries, at least not that I can think of. You can always print the generated SQL and see how it is transformed from various named queries to plain SQL. Could you try the suggestion from my edited post? If that doesn't work, I'm out of ideas. – Predrag Maric Oct 30 '14 at 16:20
  • @PredragMaric I run your edited post. That still doesn't work. Thanks for your effort. – Bryan Oct 30 '14 at 16:54