Handling null values from database in spring jpa - java

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.

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);

jpql variable that can be null

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")

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)

How to set query parameters with single quotes

I'm using a oracle database.I need to run a update query through jpa repository.This is the query I have tried to execute.
#Transactional(propagation = Propagation.REQUIRES_NEW)
#Modifying
#Query(
value = "UPDATE transactionlog SET transactionstatus= :ps,startedat = CURRENT_TIMESTAMP, readytoprocessat= (CURRENT_TIMESTAMP+ interval ':to' second) WHERE logid IN (:li) ",
nativeQuery = true)
public Integer reserve(#Param("ps") short processingStatus, #Param("li") List<Integer> logIdList, #Param("to") int timeOut);
But this exception
org.springframework.dao.InvalidDataAccessApiUsageException: Parameter with that name [to] did not exist; nested exception is java.lang.IllegalArgumentException: Parameter with that name [to] did not exist
But if i change this method as follows, it works fine.
#Transactional(propagation = Propagation.REQUIRES_NEW)
#Modifying
#Query(
value = "UPDATE transactionlog SET transactionstatus= :ps,startedat = CURRENT_TIMESTAMP, readytoprocessat= (CURRENT_TIMESTAMP+ interval '5' second) WHERE logid IN (:li) ",
nativeQuery = true)
public Integer reserve(#Param("ps") short processingStatus, #Param("li") List<Integer> logIdList);
Any idea?
Parameter with name [to] doesn't exist because you put :to between single quotes. Use :to instead of ':to'.
That being said, this will not work anyway. I faced really similar issue and after some hours finally found a solution which I present in answer here. For some reason, when interval comes into play injection of parameters doesn't work as you would expect.
Considering conclusion from the link above - I believe this should work:
#Transactional(propagation = Propagation.REQUIRES_NEW)
#Modifying
#Query(value = "UPDATE transactionlog SET transactionstatus= :ps,
startedat = CURRENT_TIMESTAMP,
readytoprocessat= (CURRENT_TIMESTAMP + (( :to ) || 'second')\\:\\:interval)
WHERE logid IN (:li) ",nativeQuery = true)
public Integer reserve(#Param("ps") short processingStatus, #Param("li") List<Integer> logIdList, #Param("to") int timeOut);
replace the :ps and all other parameter with ?1, ?2, ... and make the methos parameter match SQL parameter (the order will be important) .
I have found an answer for this problem.
#Transactional(propagation = Propagation.REQUIRES_NEW)
#Modifying
#Query(
value = "UPDATE transactionlog SET transactionstatus= :ps,startedat = CURRENT_TIMESTAMP, readytoprocessat= CURRENT_TIMESTAMP+ NUMTODSINTERVAL( :to, 'SECOND' ) WHERE logid IN (:li) ",
nativeQuery = true)
public Integer reserve(#Param("ps") short processingStatus, #Param("li") List<Integer> logIdList, #Param("to") int timeOut);
readytoprocessat= (CURRENT_TIMESTAMP+ interval ':to' second)
This clause is having issue, try to perform it one line prior, separately. Then you would be able to see the problem yourself.

Java Query and Enum

I have a problem this getting result list from query. Query return me an null Object. I dont have any idea why its happen. But if I comment its WHERE statement its work fine, but i have two Enum that can specify the result. I dosent think that Im first with it, and google didnt give any answer except to use NamedQuery. This is my code :
#Transactional(readOnly = true, propagation = Propagation.REQUIRED)
public DeviceProfileAttribute getRandomDeviceProfileAttribute(Category category, Platform platform) {
Query q = em.createQuery("SELECT d FROM DeviceProfileAttribute d " +
"WHERE d.tenantAttribute.attribute.category=:category AND " +
"d.tenantAttribute.attribute.platform=:platform " +
"ORDER BY RAND()");
q.setParameter("category", category);
q.setParameter("platform", platform);
q.setMaxResults(1);
if (q.getResultList().isEmpty()) {
return null;
} else {
return (DeviceProfileAttribute) q.getResultList().get(0);
}
}
Im sure that null isnt only one answer.
Thanks in advance.
P.S May be somebody now to check this query after puting all parameters ?
P.P.S The problem is in using Enum and ORDER by RAND() in one SQL Query.
The only way out for me, is to use such code :
#Transactional(readOnly = true, propagation = Propagation.REQUIRED)
public DeviceProfileAttribute getRandomDeviceProfileAttribute(Category category, Platform platform) {
Query q = em.createQuery(
"SELECT d FROM DeviceProfileAttribute d " +
"WHERE d.tenantAttribute.attribute.category=:category AND " +
"d.tenantAttribute.attribute.platform=:platform "
);
q.setParameter("category", category);
q.setParameter("platform", platform);
if (q.getResultList().isEmpty()) {
return null;
} else {
return (DeviceProfileAttribute) q.getResultList().get( new Random().nextInt(q.getResultList().size()));;
}
}

Categories