HQL - COUNT on SELECT clause not working - java

I'm trying to create an HQL query that calculates several stats on a list on specific users for specific dates (for example, how many were logged in etc.). Each stat has a different criteria):
SELECT
COUNT(SELECT u2.id FROM User u2 WHERE u.id = u2.id AND u2.lastLoginDate BETWEEN x AND y),
COUNT(some other stats), ...
FROM User u
WHERE u.managerId IN (...)
While the COUNT(SELECT...) clause worked in MySQL, in HQL I get the following exception:
org.hibernate.hql.internal.ast.QuerySyntaxException: unexpected token: SELECT
Anyone know how to make it work?
EDIT:
Ok, So according to this suggestion HQL - COUNT on SELECT clause not working now the query looks something like this:
SELECT
COUNT(DISTINCT CASE WHEN u.lastLogin BETWEEN x AND y THEN u.id ELSE null END),
COUNT(DISTINCT CASE WHEN ... END),
SUM(CASE WHEN u.id = p.userId THEN p.amount ELSE null END),
FROM User u, Points p,... WHERE u.managerId IN (...)
Problem is, the calculated SUM is not correct because of the From clause - it's multiplied by the number of tables.
For example if the total sum should be 80, and I have 4 tables in the from clause, the sum is instead 480!
Obliviously, distinct won't work correctly for sum. Any ideas?

It seems that you just want conditional aggregation. It is unclear what you really want from the query, but this may be close:
SELECT COUNT(*) as cnt1,
SUM(CASE WHEN u2.lastLoginDate BETWEEN x AND y THEN 1 ELSE 0 END) as cnt2,
SUM(CASE WHEN some other stats THEN 1 ELSE 0 END), ...
FROM User u
WHERE u.managerId IN (...)
I am surprised that count(select . . . ) worked in MySQL. First, subqueries usually require their own parentheses. Second, in a select, a subquery generally needs to be a scalar subquery. And third, subqueries are generally not allowed as arguments to aggregation functions. Are you sure the construct wasn't select count() . . .?

You can use native SQL.
Here is an example.
String sql = "select {user.*} from User user";
// here creates a hql query from sql
SQLQuery query = session.createSQLQuery(sql);
query.addEntity("user", User.class);
List results = query.list();
//Hibernate modifies the SQL and executes the following command against the database:
select User.id as id0_, User.name as name2_0_ from User user
you can search for Native SQL.
Hope this will help.

Related

How to convert SQL into HQL having union and inner joins?

I wan to convert this plain SQL query to HQL. I have an idea that how to convert SQL queries into HQL queries but my query is containing UNION and then inner join. So it become tricky to convert it for me.
select count(distinct T.id_veicolo) from
((select distinct C.id_veicolo from (
(select distinct id_veicolo from movement
where id_sede_uscita = 23
and annullato <> 'true'
and (((inizio >= '2019-01-05' and inizio <= '2019-01-06') and fine >= '2019-01-06')
or (inizio <= '2019-01-05' and (fine <= '2019-01-06' and fine >= '2019-01-05'))
or (inizio <= '2019-01-05' and fine >= '2019-01-06') or (inizio >= '2019-01-05' and fine <= '2019-01-06')))
UNION
(select id_veicolo from freeVehicle where id_sede = 23 and inizio <= '2019-01-05' and fine >= '2019-01-06') ) as C) as D
inner join (select id from parco_veicoli where targa = 'XXX') as R on D.id_veicolo = R.id) as T
Problem Point
If there is only UNION present in this query then i could convert this query into 2 sub queries but my problem is that after the UNION we again using the inner join. Which is the main problem point for me.
My Requirement
I want same the result from HQL query as i am getting from this SQL query. I do not want to use SQL query because SQL queries doesn't support caching.
In case you need strictly union with inner join then better go for native sql.The resultset can be convereted into POJO objects in a java class.But you will leverage the speed of a native query execution and also just a single db hit.

Is there equivalent clause in hibernate to Oracle 12c's "fetch first ..." clause? [duplicate]

In Hibernate 3, is there a way to do the equivalent of the following MySQL limit in HQL?
select * from a_table order by a_table_column desc limit 0, 20;
I don't want to use setMaxResults if possible. This definitely was possible in the older version of Hibernate/HQL, but it seems to have disappeared.
This was posted on the Hibernate forum a few years back when asked about why this worked in Hibernate 2 but not in Hibernate 3:
Limit was never a supported clause
in HQL. You are meant to use
setMaxResults().
So if it worked in Hibernate 2, it seems that was by coincidence, rather than by design. I think this was because the Hibernate 2 HQL parser would replace the bits of the query that it recognised as HQL, and leave the rest as it was, so you could sneak in some native SQL. Hibernate 3, however, has a proper AST HQL Parser, and it's a lot less forgiving.
I think Query.setMaxResults() really is your only option.
// SQL: SELECT * FROM table LIMIT start, maxRows;
Query q = session.createQuery("FROM table");
q.setFirstResult(start);
q.setMaxResults(maxRows);
If you don't want to use setMaxResults() on the Query object then you could always revert back to using normal SQL.
The setFirstResult and setMaxResults Query methods
For a JPA and Hibernate Query, the setFirstResult method is the equivalent of OFFSET, and the setMaxResults method is the equivalent of LIMIT:
List<Post> posts = entityManager.createQuery("""
select p
from Post p
order by p.createdOn
""")
.setFirstResult(10)
.setMaxResults(10)
.getResultList();
The LimitHandler abstraction
The Hibernate LimitHandler defines the database-specific pagination logic, and as illustrated by the following diagram, Hibernate supports many database-specific pagination options:
Now, depending on the underlying relational database system you are using, the above JPQL query will use the proper pagination syntax.
MySQL
SELECT p.id AS id1_0_,
p.created_on AS created_2_0_,
p.title AS title3_0_
FROM post p
ORDER BY p.created_on
LIMIT ?, ?
PostgreSQL
SELECT p.id AS id1_0_,
p.created_on AS created_2_0_,
p.title AS title3_0_
FROM post p
ORDER BY p.created_on
LIMIT ?
OFFSET ?
SQL Server
SELECT p.id AS id1_0_,
p.created_on AS created_on2_0_,
p.title AS title3_0_
FROM post p
ORDER BY p.created_on
OFFSET ? ROWS
FETCH NEXT ? ROWS ONLY
Oracle
SELECT *
FROM (
SELECT
row_.*, rownum rownum_
FROM (
SELECT
p.id AS id1_0_,
p.created_on AS created_on2_0_,
p.title AS title3_0_
FROM post p
ORDER BY p.created_on
) row_
WHERE rownum <= ?
)
WHERE rownum_ > ?
The advantage of using setFirstResult and setMaxResults is that Hibernate can generate the database-specific pagination syntax for any supported relational databases.
And, you are not limited to JPQL queries only. You can use the setFirstResult and setMaxResults method seven for native SQL queries.
Native SQL queries
You don't have to hardcode the database-specific pagination when using native SQL queries. Hibernate can add that to your queries.
So, if you're executing this SQL query on PostgreSQL:
List<Tuple> posts = entityManager.createNativeQuery(
SELECT
p.id AS id,
p.title AS title
from post p
ORDER BY p.created_on
""", Tuple.class)
.setFirstResult(10)
.setMaxResults(10)
.getResultList();
Hibernate will transform it as follows:
SELECT p.id AS id,
p.title AS title
FROM post p
ORDER BY p.created_on
LIMIT ?
OFFSET ?
Cool, right?
Beyond SQL-based pagination
Pagination is good when you can index the filtering and sorting criteria. If your pagination requirements imply dynamic filtering, it's a much better approach to use an inverted-index solution, like ElasticSearch.
If you don't want to use setMaxResults, you can also use Query.scroll instead of list, and fetch the rows you desire. Useful for paging for instance.
You can easily use pagination for this.
#QueryHints({ #QueryHint(name = "org.hibernate.cacheable", value = "true") })
#Query("select * from a_table order by a_table_column desc")
List<String> getStringValue(Pageable pageable);
you have to pass new PageRequest(0, 1)to fetch records and from the list fetch the first record.
You need to write a native query, refer this.
#Query(value =
"SELECT * FROM user_metric UM WHERE UM.user_id = :userId AND UM.metric_id = :metricId LIMIT :limit", nativeQuery = true)
List<UserMetricValue> findTopNByUserIdAndMetricId(
#Param("userId") String userId, #Param("metricId") Long metricId,
#Param("limit") int limit);
String hql = "select userName from AccountInfo order by points desc 5";
This worked for me without using setmaxResults();
Just provide the max value in the last (in this case 5) without using the keyword limit.
:P
My observation is that even you have limit in the HQL (hibernate 3.x), it will be either causing parsing error or just ignored. (if you have order by + desc/asc before limit, it will be ignored, if you don't have desc/asc before limit, it will cause parsing error)
If can manage a limit in this mode
public List<ExampleModel> listExampleModel() {
return listExampleModel(null, null);
}
public List<ExampleModel> listExampleModel(Integer first, Integer count) {
Query tmp = getSession().createQuery("from ExampleModel");
if (first != null)
tmp.setFirstResult(first);
if (count != null)
tmp.setMaxResults(count);
return (List<ExampleModel>)tmp.list();
}
This is a really simple code to handle a limit or a list.
Criteria criteria=curdSession.createCriteria(DTOCLASS.class).addOrder(Order.desc("feild_name"));
criteria.setMaxResults(3);
List<DTOCLASS> users = (List<DTOCLASS>) criteria.list();
for (DTOCLASS user : users) {
System.out.println(user.getStart());
}
Below snippet is used to perform limit query using HQL.
Query query = session.createQuery("....");
query.setFirstResult(startPosition);
query.setMaxResults(maxRows);
You can get demo application at this link.
You can use below query
NativeQuery<Object[]> query = session.createNativeQuery("select * from employee limit ?");
query.setparameter(1,1);
#Query(nativeQuery = true,
value = "select from otp u where u.email =:email order by u.dateTime desc limit 1")
public List<otp> findOtp(#Param("email") String email);

HQL not return results but when queried directly it works

I am trying below query in HQL but I get no results. Can someone help me on why I don't get any results? I tried to query the DB directly (please see SQL below) and i get 12 records. But HQL gives me 0 records.
"cause" I input the following String - "'XXX1','YYY 2'"
DB I use is Pracle 11g.
String queryStr = "from DefectsTran t join t.defects d where d.releaseName=:rel and t.defectCause in :cause and t.latestRecord=:lastrec";
Query q = session.createQuery(queryStr);
q.setString("rel", release);
q.setString("cause", filter2);
q.setString("lastrec", "Y");
SQL query that works fine when I use in TOAD.
select count(*)
from QC10.defects_tran t
inner join QC10.defects on DEFECT_ID_FK_DT = RECORD_ID
where
DEFECT_CAUSE in ('Data Request Issue', 'Functioning as Expected', 'User Education Required', 'Test Script Incorrect', 'Test Specific')
and t.latest_record = 'Y'
Instead of q.setString("cause", filter2); use q.setParameterList("cause", filter2);. filter2 has to be of a Collection subtype. Please read more about other overloading available for setParameterList: http://docs.jboss.org/hibernate/orm/3.2/api/org/hibernate/Query.html

(JPQL) - Query for getting user with highest number of records

Excuse me for anking again about this issue but I need to have a JPA query for this:
select username, count(*)
from Records
group by username
order by count(*) desc
limit 1
I thought about smth like:
select r.username,count(*) from Records r order by r.username desc
and then to call
getResultList().get(0)
but I am allowed to write only:
select r from Records r order by r.username desc
and in this case I do not know how to get what I need.
Does anyone have any idea?
The SQL query has a group by, and orders by count. The JPA query doesn't have any group by and orders by user name. So I don't see how they could return the same thing.
The equivalent JPQL query is
select r.username, count(r.id)
from Record r
group by r.username
order by count(r.id) desc
If you call setMaxResults(1) on the Query object, the limit clause will be added to the generated SQL query, making it completely equivalent.

CASE statement in HQL or Criteria

derived from this question, is it possible to use HQL or Criteria for the following SQL statement:
SELECT
e.type,
count(e),
count(d),
count (case when gender = 'male' then 1 else NULL end) AS NumberOfMaleEmployees
from Department d
JOIN d.employees e
WHERE e.dead = 'maybe'
GROUP BY e.type
Although google comes up with a few hits that state HQL supports CASE statements, Hibernate 3.6.6 fails with a
QuerySyntaxException: unexpected token: CASE
when I create the query above on an EntityManager instance.
How much of a bad idea is it, to create another query for every e.type to determine the number of males manually, e.g. for every e.type
SELECT
count(e),
from Department d
JOIN d.employees e
WHERE e.dead = 'maybe', e.type = ugly
Since there could be quite a few types, this is potentially slow. I'd like the database to do the work for me.
Well, it seems, case statements are supported:
http://docs.jboss.org/hibernate/core/3.5/reference/en/html/queryhql.html
They just don't seem to work within count(). An alternative would be using sum(case when... then 1 else 0 end) instead

Categories

Resources