JPA SQLSyntax missing expression error - java

I have a Java EE application that has a named Query in an entity called download.
The named query is:
#NamedQuery(name="Users.DownloadCount",
query="SELECT u.fullName, count(u.downloads) FROM WebUser u "
+ "JOIN u.downloads ud WHERE ud.downloadTime "
+ "> :startDate AND ud.downloadTime < :endDate")
I attempt to do the following query in one of my methods:
List<Object[]> downloads = new ArrayList();
if(userName==null){
downloads = manager.createNamedQuery("Users.DownloadCount").setParameter("startDate", sDate,TemporalType.DATE)
.setParameter("endDate", eDate,TemporalType.DATE).getResultList();
The date variables sDate and eDate are:
Date sDate = format.parse(startDate);
Date eDate = format.parse(endDate);
For some reason however I seem to be getting a :
java.sql.SQLSyntaxErrorException: ORA-00936: missing expression
exception.
I have pulled the query out of the entity class and done a normal entitymanager query call but I still get the same exception.

Add a blank between ud.downloadTime and > :s and between ud.downloadTime and < !

I realised the problem:
First problem: Missing a group by with the aggregate function
Second problem: Not joining the entities WebUser and Downloads so no way for the function to count the amount of related entities.
Query should be:
"SELECT u.fullName, count(d) FROM WebUser u JOIN u.downloads d "
+ "WHERE d.downloadTime > :startDate AND d.downloadTime< :endDate"
+ " GROUP BY u.fullName"

Related

Hibernate NamedQuery UPDATE statement throws QuerySyntaxException

I encountered a very interesting issue. I want to create an UPDATE statement NamedQuery for my class (I know this is a bit hacky).
The weird thing is if I use positioned parameters in the query like ?1, ?2 etc. it works perfectly. However, if I want to use named parameters like :id, it fails with the following error:
failed because of: org.hibernate.hql.internal.ast.QuerySyntaxException: expecting EOF, found 'consumingTxId' near line 1.
I'm using Kotlin btw.
My entity:
class StateDetailEntity(
#Id
#Column(name = "issue_tx_id")
val issueTxId: String,
#Column(name = "consuming_tx_id")
val consumingTxId: String?
)
My named query:
NamedQuery(
name = "StateDetailEntity.consume",
query = "UPDATE StateDetailEntity SET " +
"consumingTxId = :consumingTxId " +
"WHERE issueTxId = :issueTxId " +
"AND consumingTxId IS NULL"
)
If I add an alias to the table and re-write the name query to like UPDATE StateDetailEntity s SET... I get the following error:
org.hibernate.hql.internal.ast.QuerySyntaxException: expecting EOF, found 's'.
The funny thing is if I change the query like this:
NamedQuery(
name = "StateDetailEntity.consume",
query = "UPDATE StateDetailEntity SET " +
"consumingTxId = ?1 " +
"WHERE issueTxId = ?2 " +
"AND consumingTxId IS NULL"
)
There are no issues at all. Any ideas? Using Hibernate 5.4.32.Final.

sql to hql throwing exception

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.

JPA Hibernate JOIN ... ON ... AND

I use the following query:
SELECT * FROM phaseinproject as pip JOIN projectinrelease pir
ON pip.projectInRelease_id = pir.id
JOIN releaseperiod as rp ON pir.release_id = rp.id
JOIN releasestructure as rs ON rs.id = rp.releaseStructure_id
JOIN phaseinreleasestructure as pirs ON pirs.releaseStructure_id = rs.id
JOIN releasephase as rlp ON rlp.id = pirs.phase_id
AND rlp.id = pip.phase_id
This query works totally fine. I get three results (the amount I expect).
I convert this query to the following HQL query:
TypedQuery<PhaseInProjectOverview> findPhasesInRelease = em.createQuery("SELECT NEW nl.dashboard.dto.out.PhaseInProjectOverview(phaseInProject.id, phase.name, phaseInProject.startDate, phaseInProject.plannedEndDate, phaseInProject.endDate) FROM PhaseInProject phaseInProject "
+ "JOIN phaseInProject.projectInRelease projectInRelease "
+ "JOIN projectInRelease.release release "
+ "JOIN release.releaseStructure releaseStructure "
+ "JOIN releaseStructure.phaseInReleaseStructures phaseInReleaseStructure "
+ "JOIN phaseInReleaseStructure.phase phase "
+ "WHERE release.id = :releaseId ORDER BY phaseInReleaseStructure.position, phaseInProject.startDate", PhaseInProjectOverview.class);
findPhasesInRelease.setParameter("releaseId", releaseId);
return findPhasesInRelease.getResultList();
No matter what I try: I get 6 results, because HQL does not seem to support the "JOIN ... ON ... AND ..." sql syntax.
Does anyone know how to solve this problem?
edit:
I added my own answer with the used solution. Thank you all for the answers/pointers.
Try the with keyword: phaseInReleaseStructure.phase phase WITH phase.id = phaseInProject.phase_id - this should result in SQL like releasephase as rlp ON rlp.id = pirs.phase_id AND rlp.id = pip.phase_id
Alternatively just add that condition in the where clause:
... WHERE release.id = :releaseId AND phase.id = phaseInProject.phase_id ...
I solved my problem with an extra WHERE clause:
phase.id = phaseInProject.phase.id
Now I get the results I was expecting.
The 'WITH' keyword does not seem to work with multiple entities. When I try, I get an exception:
HQL error: with-clause referenced two different from-clause elements
When trying to use the 'ON' syntax like JOIN phaseInReleaseStructure.phase phase ON phase.id = phaseInProject.id, I get another error:
unexpected token: ON near line 1, column 473

NamedQuery select first in list

I try to make a namedQuery:
#NamedQuery(name = "Interval.findByMemoryType",
query = "select i from Interval i JOIN i.intervalDatas id "
+ "where id.fragments.memoryType = :memoryType")
My problem is, that fragments is a list of fragment. I'm only interested in memory type of first element in the list.
So I should have something like this:
#NamedQuery(name = "Interval.findByMemoryType",
query = "select i from Interval i JOIN i.intervalDatas id "
+ "(select first(id.fragments)) as fid) where fid.memoryType = :memoryType")
But I get always “The query contains a malformed ending” problem.
Could somebody help me??
You can take the first result:
TypedQuery<Interval> q = em.createQuery ("Interval.findByMemoryType", Interval.class);
q.setParameter("memoryType", memoryType);//+other parameters if you have
Interval interval = q.getSingleResult();
The small disadvantage is that it may load all its intervalDatas (depending on the mapping). Also check the documentation for possible exceptions.

How can I execute an update (named query) in Hibernate Template?

I have a namedQuery like this:
#NamedQueries ({ ...
#NamedQuery(name = "myUpdate", query = "update User set country = 'EN' where user.id = :id")
...
})
In dao layer
getHibernateTemplate().bulkUpdate(...?)
UPDATE
Query query = sessionFactory.getCurrentSession.getNamedQuery("myUpdate");
getHibernateTemplate.bulkUpdate(query.getQueryString(), id);
I get an error:
Hibernate: update User, set country=EN where id = 2343 ORA-00971: missing SET keyword
Anybody now how can resolve this problem?
UPDATE 2
#NamedQuery(name = "myUpdate", query =
"update User set country = 'EN' where
user.profile.id = ?")
OK
#NamedQuery(name = "myUpdate", query =
"update User set country = 'EN' where
user.profile.name = ?")
NOT OK :(
Unfortunately, that feature is missing in spring, as the named queries are supposed to be used only to retrieve data. One thing you can do is (this is a bit of a work around)
Session session = getHibernateTemplate().getSession();
Query query = session.getNamedQuery("myUpdate");
String update = query.getQueryString();
getHibernateTemplate().bulkUpdate(update, [params]);
I would put that in some kind of helper, so your DAO logic doesn't have to go around spring too.
edit
there's a dangling comma between User and set "update User , set country=EN where"
Actually this is a very old question but I had the same problem today. I realized that the update does not work since you cannont have a join inside of a simple UPDATE. That is also the reason why the comma is added. Hibernate tries to rewrite the query like this:
UPDATE User u, Profile p SET u.country = 'EN' where p.name = ? AND p.id = u.profile.id
To solve the issue you need to select the ids from the second table yourself.
#NamedQuery(name = "myUpdate", query = ""
+ " UPDATE User u "
+ " SET country = 'EN' "
+ " WHERE u.profile.id IN ( "
+ " SELECT p.id "
+ " FROM Profile p "
+ " WHERE p.name = ? "
+ " )"

Categories

Resources