11

In a Spring Boot application, I have a SQL query that is executed on a postgresql server as follows :

@Query(value = "select count(*) from servers where brand= coalesce(?1, brand) " + "and flavour= coalesce(?2, flavour) ; ", nativeQuery = true) Integer icecreamStockCount(String country, String category); 

However,

I get the following error when I execute the method :

ERROR: COALESCE types bytea and character varying in PostgreSQL

How do I pass String value = null to the query?

**NOTE : ** I found that my question varied from JPA Query to handle NULL parameter value

3
  • I don't know if that's possible or not. I'd try creating separate queries if certain inputs are null. Commented Mar 15, 2017 at 14:47
  • FWIW There is no JPA @Query annotation. That is Spring. Commented Mar 15, 2017 at 14:50
  • you can just change coalesce to case when then else end, like "select count(*) from servers where brand = (case when ?1 = 'undefined' then brand else ?1::bytea) " Commented Mar 15, 2017 at 15:01

5 Answers 5

7

When I encounted this error, I ended up using a combination of OR and CAST to solve the issue.

SELECT COUNT(*) FROM servers WHERE (?1 IS NULL OR brand = CAST(?1 AS CHARACTER VARYING)) AND (?2 IS NULL OR flavour = CAST(?2 AS CHARACTER VARYING)) 

This works even if ?1, ?2, brand and flavor are all nullable fields.

Note that passing null for ?1 means "all servers regardless of brand" rather than "all servers without a brand". For the latter, you could use IS DISTINCT FROM as follows.

SELECT COUNT(*) FROM servers WHERE (CAST(?1 AS CHARACTER VARYING) IS NOT DISTINCT FROM brand) AND (CAST(?2 AS CHARACTER VARYING) IS NOT DISTINCT FROM flavour) 

Finally, certain parameter types such as Boolean cannot be cast in SQL from BYTEA to BOOLEAN, for those cases you need a double cast:

SELECT COUNT(*) FROM servers WHERE (?1 IS NULL OR is_black = CAST(CAST(?1 AS CHARACTER VARYING) AS BOOLEAN)) 

In my eyes this is a problem in Hibernate which could be solved by passing Java null parameters as plain SQL NULLs rather than interpreting null as a value of type BYTEA.

Sign up to request clarification or add additional context in comments.

Comments

6

You need not coalesce, try this

@Query("select count(*) from servers where (brand = ?1 or ?1 is null)" + " and (flavour = ?2 or ?2 is null)") Integer icecreamStockCount(String country, String category); 

2 Comments

@Jodiug, do you check it? This is an issue that TS came with.
Deleted my comment. It seems from my small experiments that OR can be performant as long as only a single clause uses data from the database. So ?1 = 3 OR x = ?1 can be indexed but ?1 = x OR x = 22 cannot be indexed. So your answer works according to that rule. My mistake!
3

So this is not the exact answer to the question above, but I was facing a similar issue, I figured I would add it here, for those that come across this question.

I was using a native query, in my case, it was not a singular value like above, but I was passing in a list to match this part of the query:

WHERE (cm.first_name in (:firstNames) OR :firstNames is NULL) 

I was getting the bytea error, in the end I was able to send an empty list.

(null == entity.getFirstName()? Collections.emptyList() : entity.getFirstName()) 

In this case, sending the empty list to the resolver worked, where as null did not.

hope this saves you some time.

Comments

1

If you really need to use native query, there is a problem because it's an improvement not implemented yet, see hibernate. If you don't need to use native you can do (where ?1 is null or field like ?1). Assuming you do need native, you may treat the String before by setting this empty and then calling the repository and this one would be like:

@Query(value = "select count(*) from servers where (?1 like '' or brand like ?1) " + "and (?2 like '' or flavour like ?2)", nativeQuery = true) Integer icecreamStockCount(String country, String category); 

There is always javax.persistence.EntityManager bean as option for native query situations and I recommend it instead of previous approach. Here you can append to your query the way you want, as follows:

String queryString = "select count(*) from servers "; if (!isNull(country)) queryString += "where brand like :country"; Query query = entityManager.createNativeQuery(queryString); if (!isNull(country)) query.setParameter("country", country); return query.getResultList(); 

Observations:

  1. Newer versions have improved this '+' concatenation Strings. But you can build your queryString the way you want with StringBuilder or String Format, it doesn't matter.
  2. Be careful with SQL injection, the setParameter method avoid this kind of problem, for more information see this Sql Injection Baeldung

Comments

0

null parameters are not allowed before Hibernate 5.0.2. See https://hibernate.atlassian.net/browse/HHH-9165 and the replies to https://www.postgresql.org/message-id/[email protected]

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.