Hibernate null parameter native query Spring Boot - spring

I am trying to pass a null parameter (:searchQuery) to a native query in Spring Boot, I have tried various different ways but whenever I pass null I get the error
ERROR: operator does not exist: text ~~ bytea
Query
#Query(value = "SELECT count(*) FROM analysis_history ah, analysis_group ag WHERE ah.analysis_group_id = ag.id "
+ "AND ah.creation_date BETWEEN :from AND :to AND ((:searchQuery IS NULL) OR (:searchQuery IS NOT NULL AND ag.name LIKE :searchQuery))", nativeQuery = true)
Long count(#Param("from") Date from, #Param("to") Date to, #Param("searchQuery") String searchQuery);
Can anyone help?

You cannot use like null
SELECT count(*) FROM analysis_history ah, analysis_group ag
WHERE ah.analysis_group_id = ag.id AND ah.creation_date
BETWEEN :from AND :to AND ag.name LIKE :searchQuery
And you pass '%' in the searchQuery when the searchQuery parameter is null? e.g.
repository.count(from, to, searchQuery == null ? "%" : searchQuery);

There is a way to bypass this, but you need access to the EntityManager, and not use the #Query annotation to create that implementation for you.
Long count(Date from,Date to, String searchQuery) {
Number n = em.createNativeQuery("... that query")
.setParameter("from", from, TemporalType.DATE) // assuming that's a Date, and not TimeStamp
.setParameter("to", to, TemporalType.DATE)
.setParameter("searchQuery", "")
.setParameter("searchQuery", searchQuery)
.getSingleResult();
return n.longValue();
}
The first call to .setParameter("searchQuery", "") tells Hibernate what type this is, the next one sets the value.
The problem comes from Postgres doing the typecheck during parsing, and not deferring the error in case the parameter set is a null.

An alternative workaround to the issue posed by #coladict, which is compatible with Query and performs as well as the original would.
SELECT count(*)
FROM analysis_history ah, analysis_group ag
WHERE ah.analysis_group_id = ag.id
AND ah.creation_date BETWEEN :from AND :to
AND (:searchQuery IS NULL OR ag.name LIKE CAST(:searchQuery AS CHARACTER VARYING))

Related

How to check the length of a JPA query parameter if it is a list?

#Query("Select DISTINCT answer from Answer answer " +
"WHERE ( answer.user = :user or (:user is null)) " +
"AND ( answer.quiz in :quizzes or (:quizzes is null))"
)
public Page<Answer> multiParam(Quiz[] quizzes, User user, Pageable pageable);
This query works as long as quizzes is provided.
User however is optional.
The problem is that when quizzes the parameter is missing, the is null check will fail with this error:
org.springframework.dao.InvalidDataAccessApiUsageException: Encountered array-valued parameter binding, but was expecting [com.quiz.models.Quiz (n/a)]; nested exception is java.lang.IllegalArgumentException: Encountered array-valued parameter binding, but was expecting [com.quiz.models.Quiz (n/a)
I want to find a way to make Quizzes the parameter as optional.
Try the following :
#Query("Select DISTINCT answer from Answer answer " +
"WHERE ( answer.user = :user or (:user is null)) " +
"AND (COALASCE(:quizzes,null) IS NULL OR answer.quiz in :quizzes))"
public Page<Answer> multiParam(List<Quiz> quizzes, User user, Pageable pageable);
COALASCE returns the first non null value , which in this case would return null when quizzes is null.
Not sure if this would work with Array type or not.
A native solution from JPA is still pending : https://github.com/spring-projects/spring-data-jpa/issues/622

How to pass operators like "<",">","=" (less than,greater than,equals to) as parameters to a native query in springboot?

For example let my query be as shown below:
String query="select * from table_name where column_name1 > ?1 and column_name2 < ?2";
#Query(value = query, nativeQuery = true)
public List<Object[]> getResult(String filterValue1,Integer filterValue2);
how to pass the operator(<,>,=) also as a parameter?
I am using postgreSQL database.
Thank you.
If you have an option to construct/concat a String prior to run the query, there is no problem:
public String methodOne(String firstOperator
, String secondOperator) {
return "select * from table_name where column_name1 "
+ firstOperator + " ?1 and column_name2 "
+ secondOperator +" ?2";
}
It is more complicated if you use SpringData repositories.
There isn't a lot you can do with native queries parameters because SpringData is looking for native SQL operators inside the query string. But you can try to do some tricks with LIKE operator and built-in functions (in SQL, sometimes >,< can be replaced with LIKE)
(not completely an answer to your question, but)
A condition that can be omitted
#Query(value =
... AND column_name LIKE IIF(:myParam <> '%', :myParam,'%')
<skipped>
... repositoryMethod(#Param("myParam") String myParam);
IIF - a ternary operator function in MSSQL, you can find something like this in your RDBMS
when you send myParam='actualValue' it will be transformed into
and column_name LIKE 'actualValue'
i.e. column_name='actualValue'
when you send myParam='%' it will be transformed into
and column_name LIKE '%'
i.e. "and TRUE"

How to use oracle NVL function in spring data repository nativeQuery

I am trying to use oracle' NVL function in nativeQuery of Spring Data Repository.
While i am passing null value in programId parameter then it is throwing exception (ORA-00932: inconsistent datatypes: expected NUMBER got BINARY) and if i am passing a valid value in "programId" then it is working properly.
public interface ProgramRulesRepository
{
public static final String FIND_PROGRAM_RULES_BY_PARTICIPANT_ID_AND_ROLE_OR_PROGRAM = " SELECT DISTINCT pr.id , pr.program_id , prgm.display_name , pr.group_id , pr.type , pr.cmmo_key FROM program prgm , program_rule pr , program_audience pa , participant_audience paa WHERE prgm.id = pa.program_id AND pr.program_id = pa.program_id AND pa.audience_id = paa.audience_id AND pr.type = :roleType AND paa.participant_id = :participantId "
+ " AND pr.program_id = NVL ( :programId ,pr.program_id )";
#Query( value = FIND_PROGRAM_RULES_BY_PARTICIPANT_ID_AND_ROLE_OR_PROGRAM, nativeQuery = true )
List<Object[]> findByParticipantIdAndRoleTypeOrProgramId( #Param( "participantId" ) Long participantId, #Param( "roleType" ) String roleType, #Param( "programId" ) Long programId );
}
Exception :
Caused by: java.sql.SQLSyntaxErrorException: ORA-00932: inconsistent datatypes: expected NUMBER got BINARY
Avoid NVL and COALESCE when using Hibernate. COALESCE function needs to have all parameters of the same type. NVL is using implicit casting which doesn't work well when there is BINARY or VARBINARY. And where this BINARY came from? Well, Hibernate is setting NULL value as type of BINARY and ignores the real datatype backed by Java. When you set logging level to trace you can see in output:
binding parameter [1] as [VARBINARY] - [null]
So when the other type of in COALESCE or NVL function is for example NUMBER, you will get that error ORA-00932.
A good solution for this problem is this:
" AND (:programId IS NULL OR pr.program_id = :programId)"
Doing this way, if your param is null this sentence will result TRUE and won't discard the register, and if it is not null will be compared with the value stored in its field.
I have faced this problem with MongoDB. But I could solve this problem by using mongoTemplate as like,
Query query = new Query();
Criteria criteria = new Criteria();
List<Criteria> orCriterias = new ArrayList<>();
if( dto.getId() != null) {
orCriterias.add(Criteria.where("id").is(Integer.parseInt(dto.getId())));
}
... so on for other fields
criteria.orOperator(orCriterias.toArray(new Criteria[orCriterias.size()]));
query.addCriteria(criteria);
List<StudentDTO> recordsList = mongoTemplate.find(query, StudentDTO.class,
"student_collection");

Spring jdbcTemplate dynamic where clause

Is it possible to generate arbitrary where condtions SQL query through Jdbc template:
example:
If i pass value for 1 parameter (only name) : search by name
"select * from address where shopname = ?";
If i pass value for 2 parameter (name and city) - search by shopname and city:
"select * from address where shopname = ? and city = ?";
I have mupliple search fields. 7 fields. If user enters any combination. i have search only based on parameter. How to dynamically pass the parameters to the sql. Need snippet/Example how to achieve this.
What you want is some sort of criteria building api, which Hibernate has. Unfortunately, I don't think Spring's JdbcTemplate has any such facility. Others will correct me if I'm wrong...
Though as some guys already suggested that Hibernate is the best way of doing this, but still i think you can try this approach-
String sql = "select * from address where 1 = 1";
if(shopname != null)
sql += "and shopname = :shopname";
if(city!= null)
sql += "and city = :city";
and so on..and use NamedParameterJdbcTemplate
Spring Data and Hibernate have that kind of functionality. Though it might not be worth dragging in such big framework for your app.
You can try to check out SimpleJdbcInsert
http://docs.spring.io/spring/docs/current/spring-framework-reference/html/jdbc.html
Edit:
Alternatively you can try to fix it in SQL with checking on empty, but if you have lots of data to go through, this technique will slow down your request.
"select * from address
where (shopname = ? or shopname = null)
and (city = ? or city = null)";
If Scala is an option to you, the query could be constructed with something like this:
case class Search(shopname:String, city:String = None) {
def sql = "select * from address where shopname = '"+shopname+"'" + city.map(" and city = '"+
_ +"'").getOrElse("")
}
Example usage:
Search("lloh").sql
Search("lloh", Some("Austin")).sql

HQL Query with multiple Criteria

I am trying to write a HQL Query which selectes rows from a table based on multiple criteria.
firstName,lastName
the catch is that the query should be flexible to ignore any empty or null values
so
select t from table t where (:firstname = '' or t.firstName = :firstName) AND
(:lastName = '' OR t.lastName = :lastName)
I would have thought this would work? But it doesnt - it never returns any rows? Any ideas what could be wrong here? I am very new to HQL thats why this question.
If I am understanding correctly you want a way to allow the user to search by firstName, lastName or both. So you should be checking if the parameter passed in is empty then don't make it a condition. If they supply all blank parameters it would return the whole table. Try:
select t from table t
where (:firstname IS NULL or t.firstName = :firstName) AND
(:lastName IS NULL OR t.lastName = :lastName)
(:firstname = '' or t.firstName = :firstName)
Your criteria is strange. If :firstname = '' and if a firstname (t.firstName) is equal '' in the database, the criteria t.firstName = :firstName is good ('' = '')
You don't need :firstname = ''
But If you want to check null value, you need to do:
t.firstName IS NULL or t.firstName = :firstname
What happens if you run following hql with firstname parameter set to empty string?
select t from table t where (:firstname = '')
and following with firstname parameter set to null:
select t from table t where (:firstname is null)
If any of the above returns the whole table then the HQLs named parameter might support what you are trying to do.
Otherwise you must use different queries for the null parameter cases. You can do this by generating the query dynamically.
I had a similar requirement. I want dynamic but I'm using a tool that just gives an HQL editor, so no Java.
The query below allows for optional parameters. Essentially a pseudo-quazi XOR of sorts . . . wish there was real XOR :/
With this query you just put NA into a param instead of leaving it empty if it is not needed.
Yeah, yeah, yeah . . . it's ugly, but it works and it's easy to alter to any other scenario needing optional params in pure HQL.
SELECT t AS table
FROM Table t
WHERE (t.valSet = :valSet
AND (:category= 'NA' AND :subCategory= 'NA'))
OR (:category != 'NA'
AND (t.valSet = :valSet
AND t.category= :category))
OR (:subCategory != 'NA'
AND (t.valSet = :valSet
AND t.subCategory = :subCategory ))

Resources