I have the following HQL and I would like to know how it handles comparing date column to null. For example what happens in case the givenTime parameter is null. I checked and got empty results. Is it always the case? is it documented?
select mo from MyClassMO mo
where mo.creationDate is not null and mo.creationDate >= (:givenTime)
and what if the givenTime replaced with inner select query which returns null?
thanks
I know you asked about hql but for handling such a cases I prefer using something called Criteria in hibernate. It can be mixed up easy with SQL.
JPA and Hibernate - Criteria vs. JPQL or HQL
Criteria criteria = session
.createCriteria(MyClassMO.class)
.add(Restrictions.isNotNull("creationDate"));
if (givenTime!=null){
criteria.add(Restrictions.ge("creationDate", givenTime));//ge - greater equals
}
List<MyClassMO> result = criteria.list();
If your parameter is nullable, you must manage it manually.
In base of your applied logic, you can write the query.
I suppose some cases (tell me if your is one of these or another).
Case 1: Not consider givenTime in filter
You can wirte this query:
String hql = "select mo from MyClassMO mo" +
" where mo.creationDate is not null";
if (givenTime != null) {
hql += " and mo.creationDate >= (:givenTime)";
}
Case 2: if givenTime is null, put the current date
String hql = "select mo from MyClassMO mo" +
" where mo.creationDate is not null " +
" and mo.creationDate >= COALESCE(:givenTime, current_date())";
Case 3: if givenTime is in subquery returns null, put the current date
String hql = "select mo from MyClassMO mo" +
" where mo.creationDate is not null " +
" and mo.creationDate >= "
" (SELECT COALESCE(:giventime, current_date()) " +
" FROM yourtable WHERE conditions";
Related
It is necessary to select dataset using JPQL query with optional condition - comparing the field value (LocalDateTime type) with a user-specified parameter (also LocalDateTime type).
First I made a well working code:
return entityManager.createQuery(
"SELECT new com.******.*******.*******.****.models.dto.SomeDto " +
"(s.id, " +
"s.userId) " +
"s.persistDate) " +
"FROM Some s WHERE s.userId = :userId
AND s.persistDate >= :userDateTime", SomeDTO.class)
.setParameter("userId", userId)
.setParameter("userDateTime", userDateTime)
This code works but there is one problem:
this condition may exist or may not exist - dependent on app logic. Therefore, there is a need not to use injection using .setParameter (for this condition), but to form a string (which may be empty) depending on the logic and then add to the request:
String extraCondition = (userDateString.equals("false")) ? "" :
"AND s.persistDateTime >= " + userDateString;
return entityManager.createQuery(
"SELECT new com.******.*******.*******.****.models.dto.SomeDto " +
"(s.id, " +
"s.userId) " +
"s.persistDate) " +
"FROM Some s WHERE s.userId = :userId " + extraCondition, SomeDTO.class)
.setParameter("userId", userId)
But the problem is that no matter how I tried to format the userDateString variable, I get an Internal Server Error.I even tried using just a text string instead of variable (tried with different formatting):
String extraCondition = (userDateString.equals("false")) ? "" :
"AND s.persistDateTime >= 2023-01-27T23:30:50";
But the result is also bad - Internal Server Error.
I also tried using the .isAfter method instead of the ">=" operator, but that didn't help either.
How to inject LocalDateTime values comparing into query as String?
even if the date string may or may not be necesssary, you can (and should!) still use parameter injection, not formatted values.
Basically, your code should look like this:
String queryStr = ....;
boolean someCondition = <expensive_test_here>;
if(someCondition) {
queryStr += " AND s.persistDate >= :userDateTime";
}
Query q = em.createQuery(queryStr).setParameter("userId", userId);
if(someCondition) {
q.setParameter("userDateTime", userDateTime);
}
I have this sql query which I am using native sql to process currently and I need to transform it to use the JPA criteria builder so I can dynamically build up the where conditions. I am currently semi building the conditions dynamically in a janky way in sql (AND (case when 0 in :#{#filter.modelIds} then true else model_id in :#{#filter.modelIds} end)
Also, Im not sure what the best practices are and if I should just leave the code I currently have or move it over to Criteria builder. A lot of articles I read have said not to use criteria builder for advance quires. For me everything works but some people expressed concerns over the maintainability of the code. To me I like looking at sql versus the criteria builder just confuses me :)
Another blocker for me is I'm using the Postgres function generate_series to create all the dates in a date range so that way I can have empty values for dates with no data. I was not sure how to translate this over to the criteria builder I figured out how to use the date trunc function Expression<Date> view_date = builder.function("date_trunc", Date.class, builder.literal("day"), root.get("acquisitionDate")); The only other thought I had was to just execute the sub query and the use java to build up the date array and map my results to it
SQL:
select date, COALESCE(views, 0) as count, model_id
from generate_series(date '2020-05-9',date '2020-05-18',interval '1 day')as t(date)
left join (
select date_trunc('day',acquisition_date) as view_date, count(acquisition_date) as views, model_id
from view
where acquisition_date between '2020-05-9' and '2020-06-19'
group by model_id, view_date
order by view_date asc
) as v on v.view_date = date
order by date asc;
Repository:
#Query(
value = "select date, COALESCE(views, 0) as count, d.model_id as itemId " +
"from generate_series(date (:#{#filter.startDate}),date (:#{#filter.endDate}), CAST('1'||' '||:#{#filter.getInterval()} AS Interval))as t(date)" +
"left join (" +
"select date_trunc((:#{#filter.getInterval()}),acquisition_date) as view_date, count(acquisition_date) as views, model_id " +
"from view " +
"WHERE acquisition_date between (:#{#filter.startDate}) and (:#{#filter.endDate})" +
"AND (case when 0 in :#{#filter.modelIds} then true else model_id in :#{#filter.modelIds} end)" +
"AND (case when '0' in :#{#filter.imageIds} then true else image_id in :#{#filter.imageIds} end) " +
"AND (case when 0 in :#{#filter.objectIds} then true else object_type_id && array[(:#{#filter.getObjectArray()})] end)" +
"group by model_id, view_date " +
"order by view_date asc" +
") as v on v.view_date = date_trunc((:#{#filter.getInterval()}), date) " +
"order by date asc",
nativeQuery = true
)
List<ItemDateCountProjection> getModelViewsByDate(#Param("filter") ViewFilter filter);
Projection
public interface ItemDateCountProjection {
String getItemId();
Integer getCount();
Date getDate();
}
i'm trying to use in clause inside a jpa query but it gives Operand should contain 1 column(s) error.
Here is my query:
#Query(value = "select e from Table e where " +
"((:plantId is null and :unitId is null and :functionalLocationId is null) or" +
" (:functionalLocationId is not null and e.functionalLocation.id in (select f.id from FunctionalLocation f where f.id = :functionalLocationId)) or" +
" (:unitId is not null and :functionalLocationId is null and e.functionalLocation.unit.id in (select u.id from Unit u where u.id = :unitId)) or" +
" (:plantId is not null and :unitId is null and :functionalLocationId is null and e.functionalLocation.unit.plant.id in (select p.id from Plant p where p.id = :plantId))) and" +
"((:equipmentTagNumbers) is null or e.tagNo in (:equipmentTagNumbers)) and" +
"(:startDate is null or e.lastUpdateDate >= :startDate) and" +
"(:endDate is null or e.lastUpdateDate <= :endDate)" +
"order by e.id desc")
:equipmentTagNumbers property is a Lis<String> and if i send null for it query works as i expected but when i send actual data it gives the error.
any suggestions?
As far as i understood there is no isEmpty or length in jpa queries if your parameter is an array like in this case. #SOlsson was right (1,2,4 is null or... is not valid Sql so i decided to add an extra parameter to my query in order to check if its null or not so my last query like:
...
"((:hasEquipmentNumbers) is null or e.tagNo in (:equipmentTagNumbers)) and"
...
:hasEquipmentNumbers is a Boolean which i can assign null so if its null then nobody hurts and if its not i can run my IN clause with no trouble.
PS:I marked his answer cuz, it seemingly like an answer. But i implemented like i explain.
((:equipmentTagNumbers) is null or...
becomes
(1,2,4 is null or...
which is not proper SQL.
Instead, go like this:
#Query(value = "select e from Table e where " +
"..." +
(equipmentTagNumbers == null ? "" : "e.tagNo in (:equipmentTagNumbers)) and ") +
"..." +
"order by e.id desc")
That way equipmentTagNumbers does not affect the query when it is null.
I'm using hibernate in my project and I'm trying to convert an existing sql query from DaoImplementation class to hql,
The sql query I have is
JdbcTemplate select = new JdbcTemplate(dataSource);
String sql = "SELECT * FROM (SELECT site_id,rtc,sigplan,cycle_time,health,phase_no,phase_time,active_groups,groupscolour,ip "+
"FROM status_data where rtc>='" + fromDate + "' and rtc<'" + toDate + "' and "+
"site_id=" + SiteId + " order by rtc desc limit "+recordLimit+" )as temp ORDER BY RTC ASC";
I wrote the hql version to get data from HealthLog table as
String hql = " select f from (select h from HealthLog h where rtc>='"+fromDate+"' and rtc <'"+toDate+"' "
+ "and siteId = "+siteId+" order by rtc desc limit "+limit+" ) as f order by rtc asc ";
return super.readListByHql(hql);
But the above hql throws the following exception
org.hibernate.hql.internal.ast.QuerySyntaxException: unexpected token: ( near line 1, column 16 [ select f from (select h from com.traff.hibernate.model.HealthLog as h where rtc>='1974-08-01 14:10:00.0' and rtc <'1974-09-01 23:46:20.6' and siteId = 20 order by rtc desc limit 50000 ) as f order by rtc asc ]
at org.hibernate.hql.internal.ast.QuerySyntaxException.convert(QuerySyntaxException.java:54)
at org.hibernate.hql.internal.ast.QuerySyntaxException.convert(QuerySyntaxException.java:47)
at org.hibernate.hql.internal.ast.ErrorCounter.throwQueryException(ErrorCounter.java:79)
at org.hibernate.hql.internal.ast.QueryTranslatorImpl.parse(QueryTranslatorImpl.java:276)
at org.hibernate.hql.internal.ast.QueryTranslatorImpl.doCompile(QueryTranslatorImpl.java:180)
at org.hibernate.hql.intern
I also tried the below code snippet but that giving me wrong results
Criteria criteria = createEntityCriteria();
criteria.add(Restrictions.ge("rtc", fromDate));
criteria.add(Restrictions.lt("rtc", toDate));
criteria.add(Restrictions.eq("siteId", siteId));
criteria.setMaxResults(limit);
criteria.addOrder(Order.asc("rtc"));
criteria2 = criteria;
criteria2.addOrder(Order.desc("rtc"));
return criteria2.list();
Which is the correct way to achieve the result?
First of all, as already mentioned in the comments, you cannot do a subquery within the FROM clause in HQL.
See: Hibernate Documentation
Secondly, the limit keyword is not supported by HQL.
Usually you would use query.setFirstResult(0) and query.setMaxResults(recordLimit) methods where query has the type of the Query Interface. But since you are using the limit in a subquery, there is no way.
See: How to set a limit to inner query in Hibernate?
Some options:
Use a native SQLQuery
Since you are only sorting in the outer Query. You could only execute the inner query and sort in Java.
Example for Option 2:
Session session = factory.openSession();
Query query = session
.createQuery("FROM HealthLog "
+ "WHERE rtc >= :rtcL and rtc < :rtcG and siteId = :siteId "
+ "ORDER BY rtc DESC");
query.setParameter("rtcL", fromDate);
query.setParameter("rtcG", toDate);
query.setParameter("siteId", siteId);
query.setFirstResult(0);
query.setMaxResults(recordLimit);
List<HealthLog> res = query.list();
session.close();
Collections.sort(res, new Comparator<HealthLog>() {
public int compare(HealthLog o1, HealthLog o2) {
return o1.getRtc().compareTo(o2.getRtc());
}
});
The query above returns HealthLogs with all attributes. If you want to only retrieve specific attributes, you can add a SELECT new HealthLog(siteId,rtc,sigplan,cycle_time,...) to your Query with a fitting constructor in HealthLog.
Please note that the code snippet might not be ready to use, since i do not know your model and attribute names.
I have tried the following but not working.
String mainQuerySt = "select o.progressStatus, "
+ " CASE WHEN o.assigneeEmployee IS NOT NULL THEN o.assigneeEmployee.fullNameSt ELSE '' END as assignee "
+ " from Tt o"
em.createQuery(mainQuerySt).getResultList();
What is wrong with this? Actually, I want to show assigneeEmployee full name if it is not null and otherwise an empty string.
I am using EclipseLink v2.1 as JPA
Thanks in advnace.
EclipseLink Tutorial
You are using o.assigneeEmployee IS NOT NULL, and I assume o.assigneeEmployee is a relationship. Using dot notation forces an inner join, which then will filter out nulls. Try
String mainQuerySt = "select o.progressStatus, "
+ " CASE WHEN assigneeEmployee IS NOT NULL THEN assigneeEmployee.fullNameSt ELSE '' END as assignee "
+ " from Tt o left outer join o.assigneeEmployee assigneeEmployee"
If it does not return the results expected, then you will need to turn on SQL logging to see the SQL produced, and show the results you do expect.