jpql variable that can be null - java

I have an jpql query inside a JpaRepository like this:
#Query("select l from LogEntity l where l.codePackage = :codePackage and l.codeFile = :codeFile order by l.id desc")
public Page<LogEntity> findSimilarAngularLog (#Param("codePackage") String codePackage, #Param("codeFile") String codeFile, Pageable pageRequest);
The variable codePackage and codeFile can be NULL. But hibernate is always making something like
... where l.codePackage=? and l.codeFile=? ...
out of it. So if one or both of them are NULL, then there is l.codeFile=NULL and not l.codeFile IS NULL. And then he does not find anything at all.
If I copy the hibernate generated sql string to my MySQL console and change the =NULL to IS NULL, he will find everything.
So how do I change the behavior of hibernate jpa, so that in my #Query String the NULL will be treated correctly.
thanks a lot and greetings

You can use the below query for that:
#Query("select l from LogEntity l where ((:codePackage is null and l.codePackage is null) or l.codePackage = :codePackage) and ((:codeFile is null and l.codeFile is null) or l.codeFile = :codeFile) order by l.id desc")
But wouldn't that be a good idea to make hql through the logic rather than using it like the named queries?
A better logic would be to use an if condition, like,
if(codePackage == null) {
query += " l.codePackage is null ";
}
else {
query += " l.codePackage = :codePackage ";
}
***code***
//query execution code

WHERE ((:param is null and t.field is null) or t.field = :param)
#Query("select l from LogEntity l where ((:codePackage is null and l.codePackage is null) or l.codePackage = :codePackage ) and ((:codeFile is null and l.codeFile is null) or l.codeFile = :codeFile ) order by l.id desc")

Related

Custom Query Join in JPA using Criteria Builder API

Currently I am doing it like this:
List<Table1Entity> findAllMatchingEntities(Table1Entity table1Entity) {
String queryString = "SELECT table1.* FROM table1 "
+ "JOIN table2 t2 ON table1.id=t2.table1_id";
if (table1Entity.getName() != null) {
queryString +=" where name like ?";
}
Query query = em.createNativeQuery(queryString, Table1Entity.class);
if (table1Entity.getName() != null) {
query.setParameter(1, table1Entity.getName())
}
return query.getResultedList();
}
If I want to check more parameters in this join this will quickly turn into a lot of if statements and it would be really complicated to set parameters correctly.
I know I can check parameters with criteria Builder API like this:
if(table1Entity.getName() != null) {
table1EntitySpecification = (root, query, criteriaBuilder)
-> criteriaBuilder.like(
criteriaBuilder.lower(root
.get("name")),
("%" + table1Entity.getName() + "%")
.toLowerCase());;
}
and after that get them all with:
findAll(table1EntitySpecification) with findAll from simpleJPARepository. Now I can chain them together with .or or .and etc. and avoid setting the parameter and checking for null second time.
But how do I do join with criteria APi?
I know I can have in my #Repository something like this:
#Query(value = "SELECT table1.* FROM table1 JOIN table2 t2 ON table1.id=t2.table1_id", nativeQuery = true)
List<Table1Entity> findAllMatchingEntities(Table1Entity table1Entity);
But since name is optional (can be null) I can't just leave it in #Query.
What is the best solution here to avoid using native query and in case of having to check many parameters to avoid using if statements?
I don't know if I fully get your question, but regarding the possibility of nulls, and using the CRUD repository, you can always do a null check before like:
#Query(value = "SELECT table1.* FROM table1 JOIN table2 t2 ON table1.id=t2.table1_id WHERE table1.id is not null", nativeQuery = true)
List<Table1Entity> findAllMatchingEntities(Table1Entity table1Entity);
Depending on what you are trying to achieve, you can always compose the query with similar checks like (not related to your code):
#Query("SELECT c FROM Certificate c WHERE (:id is null or upper(c.id) = :id) "
+ "and (:name is null or upper(c.name) = :name)")
List<Table1> findStuff(#Param("id") String id,
#Param("name") String name);

Conditional AND using hibernate

I have the following query:
#NamedQuery(name = "User.findByParams", query=
"SELECT *
FROM User user
WHERE user.name type = :inputType")
And I wish to add AND statement, that will take place only if the inputs are supplied:
#NamedQuery(name = "User.findByParams", query=
"SELECT *
FROM User user
WHERE user.name type = :inputType AND (:ageInput != null AND user.age > :ageInput")
It means that if the ageInput is supplied, filter by it as well. If not- ignore this param. Any ideas?
Any ideas?
As the previous speakers wrote, you can use Criteria
Criteria criteria = createCriteria()
.add(Restrictions.eq("type", type));
if (ageInput != null) {
criteria.add(Restrictions.eq("ageInput", ageInput));
}
List<User> list = criteria.list();
or SQLQuery
String sql = "SELECT * " +
"FROM User user " +
"WHERE user.type = :inputType ";
sql += (ageInput != null) ? "AND ageInput = :ageInput " : "";
Query query = sessionFactory.getCurrentSession().createSQLQuery(sql)
.setParameter("inputType", inputType);
if(ageInput != null) {
query.setParameter("ageInput", ageInput);
}
return (List<User>) query.list();
You will have to check if ageInput is supplied or not in code and will have to call different methods accordingly.
Means if ageInput is supplied then you will have to call a method having ageInput constraint o/w call method which do not have ageInput constraint.
Alternatively, you can use predicates and execute a query.

Handling null values from database in spring jpa

This works fine:
#Repository
public interface VoteDao extends CrudRepository <Vote, Long> {
#Query(value = "select sum(points) from votes where siteuserid= ?1", nativeQuery = true)
int countBySiteUser(#Param("user") SiteUser user);
}
Except in case when there are no votes yet that the result is NULL and the problem is that I do not know how to handle that case of checking when it is Null since the query does no return anything when I ask...
System.out.println("!!!!: PROPOSAL VoteService: " + voteDao.countBySiteUser(user));
Should it print a Null value for that sysout? The DAO is supposed to answer with a NULL value, but it is not. I would be able to handle that NULL if provided, but it does not.
Thanks in advance for your help!
Use COALESCE to handle null as 0, which correspond to what you actually mean.
#Query(
value = "SELECT COALESCE(SUM(points), 0) FROM votes WHERE siteuserid = ?1",
nativeQuery = true)
int countBySiteUser(#Param("user") SiteUser user);
... or another solution with a programmatic approach:
// Integer instead of int to add the "null" handling
#Query(
value = "SELECT SUM(points) FROM votes WHERE siteuserid = ?1",
nativeQuery = true)
Integer countBySiteUser(#Param("user") SiteUser user);
Usage:
Integer count = voteDao.countBySiteUser(user);
if (count == null) {
count = 0;
}
System.out.println("!!!!: PROPOSAL VoteService: " + count);
The COALESCE solution seems better to me. But as #EJP said, it will depend on your needs.

retrieve result for an optional field in spring jpa query

I am a newbie to Spring and java, I have a scenario, where i like to query like below:
when i have a po.id value, i can search like this below:
form service :
prescriptionOrderRepository.searchPharmacyOrders(args);
I am writing my spring query like this:
#Query(value = "select po from PrescriptionOrder po WHERE po.pharmacyId = :pharmacyId "
+ "AND po.id = :idValue AND po.active = :active ORDER BY po.createdAt DESC")
Page<PrescriptionOrder> searchPharmacyOrders(#Param("pharmacyId") Long pharmacyId,
#Param("idValue") Long idValue, Pageable pageRequest, #Param("active") Boolean active);
Suppose if i have to requirement, po.id is an optional field means, how can write query for that.
I always expect the params, they're either null by default, or I get a value and it has to match. So for a parameter :idValue your query has
... AND (po.id = :idValue OR :idValue is null) ...
When e.g. you come from a Controller class, using GET variables, you can always use them as default null, or you're getting a value and it has to match.
In your example, that query in total...
WHERE (po.pharmacyId = :pharmacyId AND po.id = :idValue AND po.active = :active
Changes to:
WHERE ((po.pharmacyId = :pharmacyId OR :pharmacyId is null) AND (po.id = :idValue OR :idValue is null) AND (po.active = :active OR :active is null)

Can PreparedStatement do not take into account some conditions in WHERE clause?

For example, I have a statement
"SELECT * FROM Reports WHERE StartDate >= ? WHERE EndDate <= ? AND Performer = ?"
But sometimes some input fields on the web page are not filled, so I have to not take into account this conditions. i.e. I have no startdate filled, so statement must be
"SELECT * FROM Reports WHERE EndDate <= ? AND Performer = ?"
There are 3 different conditions. So, Do I have to write 8 different statements and DAO methods to accomplish the task? Really? Maybe there are other solutions?
Edit: I use MySQL/
Change your SQL to cater for nulls. Because you have not told us which database you are using, I will use "vanilla" SQL:
SELECT *
FROM Reports
WHERE (EndDate <= ? OR ? is null)
AND (Performer = ? OR ? is null)
Pass the parameters in twice each.
The other choice is to alter the SQL based on parameters being null (for example omitting Performer = ? from the where clause), but this can require a lot of code and testing. Iwould use the adaptable SQL and if it performs badly, then attempt something more advanced.
You dont need 8 different statements. You can build the query using if statements. For e.g.,
String query = "SELECT * FROM Reports where true";
if(startDate != null)
query = query + " and startDate <= ?";
if(endDate != null)
query = query + " and endDate <= ?";
if(performer != null)
query = query + " and performer = ?";
Hope it works for you.
No Prepared statement cannot exclude contions on its own. The query as to be contructed to avoid unnecessary conditions.
You can generate SQL using the code :
StringBuilder whereClause = new StringBuilder();
String and = "";
if(EndDate == null || EndDate.length == 0)
{
whereClause.append(your condition);
and = " and";
}
if(StartDate == null || StartDate.length == 0)
{
whereClause.append(and).append(your condition);
and = " and";
}
if(Performer == null || Performer.length == 0)
{
whereClause.append(and).append(your condition);
}
and based on you generated query you need to set the parameters to the prepared statement.

Categories