How to write an HQL query that to handle optional input parameters - java

#Query("SELECT c FROM message c"
+" WHERE (CASE WHEN a='' THEN 1=1 ELSE a= :a"
+ " WHEN b='' THEN 1=1 ELSE b= :b"
+ " WHEN c='' THEN 1=1 ELSE c= :c"
+ " WHEN d='' THEN 1=1 ELSE d= :d)")
I am trying to write an HQL query that has optional inputs of values a, b, c, and d.
My goal is to have the query recognize if an input =' 'and then just move along with 1=1 essentially doing nothing.
I keep getting error
antlr.NoViableAltException: unexpected token: =
First the error token was "CASE" and now it is "=".
Is the "1=1 approach" viable?
Can I use CASE statements like this in HQL?
Thank you
UPDATED ATTEMPT:
#Query("SELECT c "
+ "FROM message c"
+ " WHERE"
+ " CASE (WHEN a != '' THEN a = :a"
+ " WHEN b != '' THEN b = :b"
+ " WHEN c != '' THEN c = :c"
+ " WHEN d != '' THEN d = :d)"
antlr.NoViableAltException: unexpected token: WHEN
antlr.NoViableAltException: unexpected token: a
antlr.NoViableAltException: unexpected token: THEN

Are you sure that You want to use JPQL to do this operation?
I would suggest you to use Query By Example OR QueryDSL
If you want to continue using JPQL, then Read ahead :P
You can use sPEL inside #Query if you want to use conditional operator. Here is a sample query based on your requirement.
#Query("SELECT C FROM Message C " +
"WHERE 1=1 " +
"AND ((1=:#{ #param1 == null ? 1 : 0 }) OR (C.param1 = :#{#param1})) " +
"AND ((1=:#{ #param2 == null ? 1 : 0 }) OR (C.param2 = :#{#param2})) " +
"AND ((1=:#{ #param3 == null ? 1 : 0 }) OR (C.param3 = :#{#param3})) " +
"AND ((1=:#{ #param4 == null ? 1 : 0 }) OR (C.param4 = :#{#param4})) "
)
List<Message> find(#Param("param1") Long param1, #Param("param2") Long param2,#Param("param3") Long param3, #Param("param4") Long param4);
A Little Explanation
First Condition 1=1: Hehe, obsessed with conditions on next line to WHERE clause, you can remove it.
((1=:#{ #param1 == null ? 1 : 0 }) OR (C.param1 = :#{#param1})): Crazzy !!
First part will decide whether to check second part or not. Suppose param1 is null then (1=:#{ #param1 == null ? 1 : 0 }) will return 1=1 and (C.param1 = :#{#param1}) will be skipped.
Same will apply to further conditions.
Feel free to comment, if you have any doubts.
BTW, you should use fields with respect to the object inside JPQL. For example C.param1 instead of just param1.

Related

An expression of non-boolean type specified in a context where a condition is expected. SQL not returning values when executed through API

I have query that returns a result when executed in the database. But it does not return anything when I execute it from my application, it just keep on executing.
Below is the code snippet I have:
#Query(value = "select distinct c.ROWID_OBJECT, rc.CMPNY_NMBR from ecm_ors.dbo.c_b_pty rc " +
"left join ecm_ors.dbo.c_b_pty_rel rel on rc.ROWID_OBJECT = rel.PTY_ID_1 " +
"left join ecm_ors.dbo.c_b_pty c on rel.PTY_ID_2 =?1 " +
"where rc.PTY_ROLE = 'L' and c.PTY_ROLE = 'L' and rel.REL_TP = 'is parent of' " +
"and rc.HUB_STATE_IND = 1 and rel.HUB_STATE_IND = 1 and c.HUB_STATE_IND = 1 " +
"AND ?2 IS NULL OR c.CMPNY_NMBR =?2 AND rel.PTY_ID_2 = c.ROWID_OBJECT", nativeQuery = true)
List<Object[]> getCompanyList(String ptyId, String cmpNumber);
The same query shows the result when run it through the database
select distinct c.ROWID_OBJECT, rc.CMPNY_NMBR from ecm_ors.dbo.c_b_pty rc
left join ecm_ors.dbo.c_b_pty_rel rel on rc.ROWID_OBJECT = rel.PTY_ID_1
left join ecm_ors.dbo.c_b_pty c on rel.PTY_ID_2 = 493
where rc.PTY_ROLE = 'L' and c.PTY_ROLE = 'L' and rel.REL_TP = 'is parent of'
and rc.HUB_STATE_IND = 1 and rel.HUB_STATE_IND = 1 and c.HUB_STATE_IND = 1
and 02637 is null or c.CMPNY_NMBR =02637
and rel.PTY_ID_2 = c.ROWID_OBJECT
The problem I'm anticipating in this condition
?2 IS NULL OR c.CMPNY_NMBR =?2
Is it the kind of error:
An expression of non-boolean type specified in a context where a condition is expected
I'm not seeing any errors though, REST call keep rotates.
Not sure how to rectify this, please advice.

Spring Projection #Value field fails when a mapped entity returns 'null'

I've been trying to use Spring Data JPA Projections. Also using #Value annotation to concat some properties of some referenced projections.
The problem occurs when one of the referenced projections return 'null' because that particular column is null in the DB. Is there any way to scoot around this issue? i.e., if any of the referenced projections are null then is it possible to not use that projection and still use values of other projections?
Sample code:
public interface InitialProjection{
String getName();
ReferencedProjection1 getRp1();
ReferencedProjection2 getRp2();
#Value("#{target.name + ' - ' + target.rp1.name + ' - ' + target.rp2.name}")
String getDetail();
}
public interface ReferencedProjection1 {
String getName();
}
public interface ReferencedProjection2 {
String getName();
}
Also, since I'm having to use #Query annotated methods it seems to need to specify LEFT JOIN for every mapped entity. Is it required or is there a way to not having to specify all the aliases?
Sample code:
#Query("SELECT entity.name AS name, "
+ "ljrp1 AS rp1, "
+ "ljrp2 AS rp2 "
+ "FROM Entity entity "
+ "LEFT JOIN entity.rp1 ljrp1 "
+ "LEFT JOIN entity.rp2 ljrp2 "
+ "WHERE entity.isDeleted = 0 ORDER BY entity.name ASC")
List<InitialProjection> fetchAllActiveEntities();
Solution for the first part: As explained in the accepted answer, the mapped projections can be checked for null using Spring Expression Language. They are to be included in parantheses as given below.
#Value("#{target.name + ' - ' + (target.rp1 != null ? target.rp1.name : '' )+ ' - ' + (target.rp2 != null ? target.rp2.name : '')}")
If I understand your question correctly you are asking about null checks in Spring Expression Language.
So you could do:
#Value("#{target.name + ' - ' + (target.rp1 != null ? target.rp1.name : '') + ' - ' + (target.rp2 != null ? target.rp2.name : '')}")
And about outer join (in your case left join): Yes you need that to allow null vaules in the relationships.

SQL Request if criteria is not null

I am trying to write a query that performs a search according to several criteria.
My current JPA request:
#Query("SELECT c FROM Client c " +
"INNER JOIN c.entite e " +
"WHERE e.numeroLicence in (:numerosLicence) " +
"AND (UPPER(c.nom) LIKE CONCAT('%',UPPER(:nom),'%') " +
"AND (c.prenom IS NOT NULL AND UPPER(c.prenom) LIKE CONCAT('%',UPPER(:prenom),'%')) " +
"AND (c.dateCreation IS NOT NULL AND c.dateCreation > :dateCreation) " +
"AND (c.dateCreation IS NOT NULL AND c.dateModification > :dateModification) " +
"AND (c.dateCreation IS NOT NULL AND c.dateSuppression > :dateSuppression)) ")
Page<Client> recherche(#Param("numerosLicence") ArrayList numerosLicence,
#Param("nom") String nom,
#Param("prenom") String prenom,
#Param("dateCreation") ZonedDateTime dateCreation,
#Param("dateModification") ZonedDateTime dateModification,
#Param("dateSuppression") ZonedDateTime dateSuppression,
Pageable pageable);
The problem is that it automatically excludes rows where a column contains NULL.
I'll need the selection to be done on columns with data only, it will not try to execute the clause if the field value is NULL
For example, if I have a row whose firstname column is NULL, then this line is automatically excluded from the result whereas I would like it not to test this column if it is NULL
Do you have an idea?
I'm looking for a solution to avoid creating as many queries as possible combination
Sounds like you should use OR instead of AND:
c.dateCreation IS NULL
OR c.dateCreation > :dateCreation

How to Use CASE, WHEN in JPA

I'm new to JPA and have the below JPA query:
String qry = " SELECT e from Event e"
+ " WHERE e.meeting.meetingDate= :pdate"
+ " AND e.meeting.feedId IN (1,2)"
+ " AND e.status <> 'W'"
+ " AND e.meeting.status ='A'"
+ " AND e.settleStatus is NULL"
+ " AND e.offTime is NULL"
+ " AND e.eventCode >'07:45:00'"
+ " group by e.id";
And I need to add the ORDER BY clause dynamically. I use MySQL and please anybody tell me how to add below condition to my query using JPA.
part to add:
ORDER BY
CASE WHEN m.country_code ='AU' THEN e.timestamp_updated
WHEN m.country_code <> 'AU' THEN e.event_code
END DESC
how to add this query segment in to my JPA query?
Since JPA 2.0 you can use CASE WHEN in JPQL.
So you just have to do something like that:
ORDER BY
CASE
WHEN (e.property = ?) THEN 1
WHEN (e.property = ?) THEN 2
ELSE 3
END DESC
You can also use Criteria API
Criteria criteria = session.createCriteria(YourEntity.class);
if(clause){
criteria.addOrder( Order.asc("property_desired_1") );
}else{
criteria.addOrder( Order.asc("property_desired_2") );
}
criteria.list();
You can se more about criteria here, about JPQL CASE WHEN here and about Order here
This works protty god for me
My NATIVE Sql query
SELECT
another_column1, another_column2
CASE
WHEN property_column = 'AU' then value_column ELSE 0
END AS LEARN,
CASE
WHEN property_column = 'EU' then value_column ELSE 0
END AS Contribute
FROM testing.mytablename;
JpaRepository / JPQL Query: returning a List < tuple >
public interface MyTableRepository extends JpaRepository<MyTable, UUID> {
//c.columnNames are properties of MyTable class
// take care of spaces and , caracters
#Query("SELECT c.anotherColumn1, c.anotherColumn2, " +
" CASE c.propertyColumn WHEN 'AU' then c.valueColumn ELSE 0 END as learn," +
" CASE c.propertyColumn WHEN 'EU' then c.valueColumn ELSE 0 END as transfer "
"FROM MyTable AS c ")
List<Tuple> selectByPropertyCase();
}
FINALLY, if you want to MAP your query in a DTO Class
public interface MyTableRepository extends JpaRepository<MyTable, UUID> {
//make sure to create the constructor for the dto classs
#Query("SELECT SELECT new com.myprojectpath.model.MyTableClassDTO(c.anotherColumn1, c.anotherColumn2, " +
" CASE c.propertyColumn WHEN 'AU' then c.valueColumn ELSE 0 END as learn," +
" CASE c.propertyColumn WHEN 'EU' then c.valueColumn ELSE 0 END as transfer) "
"FROM MyTable AS c ")
List<MyTableClassDTO> selectByPropertyCase();
}

PreparedStatement with CONTAINS query

I have a query that will need to run 28 000 times in a row, so I thought using a preparedStatement probably is a clever idea.
Here is my query :
String requestWithFirstName = "SELECT SE.ELEMENT_ID, SE.LASTNAME||' '||SE.FIRSTNAME AS ELEMENT, (SCORE(1)+SCORE(2))/2 AS SCORE "
+ "FROM BL_SUSPICIOUS_ELEMENT SE "
+ "WHERE CONTAINS(SE.LASTNAME, 'fuzzy({' || ? || '},' || ? || ',' || ? || ', weight)' , 1)>0 "
+ "AND CONTAINS(SE.FIRSTNAME, 'fuzzy({' || ? || '},' || ? || ',' || ? || ', weight)' , 2)>0 "
+ (type > 0 ? "AND SE.ELEMENT_TYPE_ID = ?" : "")
+ " ORDER BY SCORE DESC";
Everthings worked fine until we realized that the fuzzy methods doesn't perform well for splitted words like 'pikachu is my hero' and it is advised to created, in this case, 4 fuzzy search for 'pikachu' 'is' 'my' 'hero'. Not sure if this is true, but as I will run the query 28 000 times it's a good opportunity to see it in action.
So I tried to modify the query in this manner :
"SELECT A.ELEMENT_ID, A.LASTNAME||' '||A.FIRSTNAME AS AKA, SCORE(1) AS SCORE "
+ "FROM BL_AKA A, BL_SUSPICIOUS_ELEMENT SE "
+ "WHERE CONTAINS(A.LASTNAME, ?, 1)>0 "
+ "AND SE.ELEMENT_ID = A.ELEMENT_ID "
+ (type > 0 ? "AND SE.ELEMENT_TYPE_ID = ?": "")
+ " ORDER BY SCORE DESC";
In this case, ? will be set to :
'fuzzy({Burnham},70,4,weight),fuzzy({Investors},70,4,weight),fuzzy({Trust},70,4,weight)'
The query seems fine, running on sql dev. However, with Java, I get the following error :
ORA-20000: Oracle Text error:
DRG-50900: text query parser error on line 1, column 30
DRG-50920: part of phrase not itself a phrase or equivalence
DRG-50900: text query parser error on line 1, column 30
DRG-50920: part of phrase not itself a phrase or equivalence
Any advice ?
I wonder if this is the same situation as the in statement (impossible to create a select * from pokemon where monster in (?))
Thanks !
When you use a prepared statement in java, it will set the parameter according to the method you use. So
String s = "'fuzzy({Burnham},70,4,weight),fuzzy({Investors},70,4,weight),fuzzy({Trust},70,4,weight)'";
statement.setString(s);
will be escaped again and results in:
'''fuzzy({Burnham},70,4,weight),fuzzy({Investors},70,4,weight),fuzzy({Trust},70,4,weight)'''
Try to set the parameter without the quotes.
You can create an IN (?) statement. But you will have to add a questionmark for every parameter: WHERE monster IN (?,?,?,?,?,?)...

Categories