Hibernate query with subquery in WHERE clause and multiple joins - java

I have been trying to get Hibernate to generate me a query with a subquery in its where clause. I've used this answer as a base to help me going, but this question mentioned only one table.
However, this is what I would need (in SQL):
SELECT [...]
FROM a
LEFT OUTER JOIN b on a.idb = b.idb
LEFT OUTER JOIN c on b.idc = c.idc
[...]
LEFT OUTER JOIN k out on j.idk = k.idk
WHERE k.date = (SELECT max(date) from k in where in.idk = out.idk) OR k.date is null
As I am not very used to using Hibernate, I'm having trouble specifying these inner joins while navigating in the inner constraints.
I was able to re-create the initial criteria as in the linked answer, but I can't seem to join the criteria and the rootCriteria.

If the entities are properly joined with #ManyToOne annotations, simply joining the criteria to the previous table will be enough to propagate the criteria to the whole query.
The following code seems to work properly to add the WHERE clause I'm looking for.
DetachedCriteria kSubquery = DetachedCriteria.forClass(TableJPE.class,"j2");
kSubQuery = kSubQuery.createAlias("k","k2");
kSubQuery.setProjection(Projections.max("k2.date"));
kSubQuery = kSubQuery.add(Restrictions.eqProperty("j.id", "j2.id"));
rootCriteria.add(Restrictions.disjunction()
.add(Subqueries.propertyEq("k.date",kSubQuery))
.add(Restrictions.isNull("k.date")));

Related

Create same join multiple times hibernate criteria

I am trying to create a query that uses the same join multiple times. An example would look as follows:
select * from a
join b b1 on b1.id=a.id
join c c1 on c1.id=b1.id
join b b2 on b2.id=a.id
join c c2 on c2.id=b2.id
where b1.attribute = "a" and c1.attribute = "b"
where b2.attribute = "c" and c2.attribute = "d"
I need to do this utilizing Hibernate Criteria. I was thinking that something like this should work:
Criteria criteria = createCriteria(a.class, "a"); //a would be the class representing table "a"
Criteria criteria1 = criteria.createAlias(b.class, "b1").createAlias(c.class, "c1");
Criteria criteria2 = criteria.createAlias(b.class, "b2").createAlias(c.class, "c2");
However, I am getting a QueryException: duplicate assocation path exception. I have seen this so: Hibernate Create Criteria to join the same table twice - tried 2 approach with 2 difference error, but this solution doesn't work for me I think, and it also implies it is not possible. Has anyone else had better luck? Thanks!
I think that is not possible with the legacy Hibernate Criteria API. You should rather use the JPA Criteria API, especially because the legacy Hibernate Criteria API was deprecated in Hibernate 5 and removed in Hibernate 6.

Hibernate - How to use DetachedCriteria with LEFT JOIN and addOrder()?

I have the below code which worked previously to order the values by displayOrder.
DetachedCriteria criteria = DetachedCriteria.forClass(Parameter.class);
...
criteria.addOrder(Order.asc("displayOrder"));
return (Collection<Parameter>) template.findByCriteria(criteria);
However, I had to move the displayOrder column from Parameter.class into a new class called ParameterCriteria.class, so the .addOrder line no longer works. How can I tweak the above criteria query to select from Parameter LEFT JOIN ParameterCriteria so I can order the results by displayOrder (Note: a ParameterCriteria may not exist for a particular Parameter so I'm using left join)

HQL Query having conditions on associated object's associated objects

I have following query :
FROM PatientInfo AS pi WHERE pi.customerApplicationPatient.customerApplication.customerApplicationId = :customerApplicationId AND pi.customerAppPatient.patient.patientId = :patientId
PatientInfo, CustomerApplicationPatient are associated and customerApplicationPatientId is the foreign key column.
CustomerApplicationPatient is associated with CustomerApplication , Patient with customerApplicationId, patientId as foreignkey columns.
Question : Get all the PatientInfo records given customerApplicationId and PatientId. My query is a good query in the sense, not producing too many inner joins or cross-joins ? Is there a better way of doing this ?
I really appreciate any help and hope the question is clear.
Thanks,
Sri
You should not rely on implicit joins when you need to reference them in your where clause.
So instead of:
FROM PatientInfo AS pi WHERE
pi.customerApplicationPatient.customerApplication.customerApplicationId = :customerApplicationId
AND pi.customerAppPatient.patient.patientId = :patientId
to
select pi
from PatientInfo AS pi
inner join pi.customerApplicationPatient cap
inner join cap.customerApplication ca
inner join cap.patient p
where
ca.customerApplicationId = :customerApplicationId
AND p.patientId = :patientId
The explicit joins will be translated to the exact join statements you'd expect. The previous query might not be that clever and might join the same child table twice.

How do I set a Restriction in Hibernate to target a column in either one of two joined tables?

I'm joining one table to another. The join works. I want to restrict the results to records with an "Error" message that can be in either table. When I do the following, I get no results back, yet I know there should be 2.
Criteria criteria = session.createCriteria(TableName.class);
criteria.createAlias("someList", "things");
Criterion restriction1 = Restrictions.eq("status", "Error");
Criterion restriction2 = Restrictions.eq("things.anotherStatus", "Error");
criteria.add(Restrictions.or(restriction1, restriction2));
finalList = criteria.list();
I noticed that the restrictions by themselves actually work. So, if I only do the first restriction on the original table with no alias OR if I only do the second restriction on the alias table, then I get 1 result each time.
Also, a simple join SQL query like the one below works as expected:
Select count(*)
From table1 t1
Left join table2 t2 on t1.id = t2.another_id
Where t1.status = 'ERROR' or t2.anotherStatus = 'ERROR'
How can I get this right in Hibernate?
EDIT 1: I now see that Hibernate does an Inner Join when I use the #JoinColumn annotation. How can I change it to do an Outer Join instead?
EDIT 2: Even adding #Fetch(FetchMode.JOIN) still results in an inner join! What gives? The documentation clearly says it will do an outer join. The annotation now looks like this:
#OneToMany
#JoinColumn(name="ID_FK")
#Fetch(FetchMode.JOIN)
private List<Thing> things;
Answer: use criteria.createAlias("someList", "things", JoinType.LEFT_OUTER_JOIN); instead.
Explanation: When no JoinType is specified, createAlias does an inner join by default.

Eclipselink group by is not working

How to add groupBy criteria to the code below? Because if I add criteriaQuery.groupBy(from.get(minutis.Preke_.prId)); - I get exactly the same SQL statement (without groupBy):
CriteriaBuilder cb = MinutisManager.getInstance().getCriteriaBuilder();
CriteriaQuery criteriaQuery = cb.createQuery(minutis.Preke.class);
Root<minutis.Preke> from = criteriaQuery.from(minutis.Preke.class);
from.fetch(minutis.Preke_.tiekejai, JoinType.LEFT).fetch(minutis.PrekeTiekejas_.tiekejas, JoinType.LEFT);
//criteriaQuery.groupBy(from.get(minutis.Preke_.prId));
TypedQuery<minutis.Preke> typedQuery = MinutisManager.getInstance().createQuery(criteriaQuery);
typedQuery.setFirstResult(0);
typedQuery.setMaxResults(100);
typedQuery.getResultList();
EDIT 1:
criteriaQuery.distinct(true) is not an option for me. Because that command hangs the whole statement and if I use EXPLAIN:
If I use GROUP BY on the query, then EXPLAIN is:
EDIT 2:
I get this SQL statement with and without criteriaQuery.groupBy(from.get(minutis.Preke_.prId));
SELECT ... FROM preke t1 LEFT OUTER JOIN preke_tiekejas t0 ON (t0.pr_id = t1.pr_id) LEFT OUTER JOIN tiekejas t2 ON (t2.tiek_id = t0.tiek_id) LEFT OUTER JOIN gamintojas t3 ON (t3.gam_id = t1.gam_id) LEFT OUTER JOIN google_compare t4 ON (t4.pr_id = t1.pr_id) LEFT OUTER JOIN grupe t5 ON (t5.pgs_id = t1.pgs_id) LEFT OUTER JOIN preke_kaina t6 ON (t6.pr_id = t1.pr_id) ORDER BY t1.pr_id LIMIT ?, ?
The SQL for the GROUP BY query should definitely contain a GROUP BY. Ensure that you are compiling and deploying the code correctly.
It could be a bug that the group by is ignored, as normal group by queries will not select object, but aggregated values. Check that you are using the latest release, and perhaps log a bug, or try JPQL.
In general your query does not make any sense, normally on a group by query you can only select the fields that you grouped by, or aggregation functions.
Perhaps consider batch fetching instead of join fetching.
http://java-persistence-performance.blogspot.com/2010/08/batch-fetching-optimizing-object-graph.html
I will file a bug in Eclipselink, because if I change fetch line with QueryHints, everything works:
Change this (with this line I get duplicate Preke entities with populated children entities):
from.fetch(minutis.Preke_.tiekejai, JoinType.LEFT).fetch(minutis.PrekeTiekejas_.tiekejas, JoinType.LEFT);
To this (with this lines I get unique Preke entities with populated children entities):
typedQuery.setHint(QueryHints.LEFT_FETCH, "Preke.tiekejai");
typedQuery.setHint(QueryHints.LEFT_FETCH, "Preke.tiekejai.tiekejas");
I get my desired result.
EDIT 1:
The bug really exists, now max resulsts is not working. Both cases typedQuery is identical.
typedQuery.setMaxResults(100);
System.out.println(typedQuery.getResultList().size()); //prints 73
typedQuery.setMaxResults(500);
System.out.println(typedQuery.getResultList().size()); //prints 413
No problem, I found the bug report, here it is, just in case someone else needs it.Criteria api ignores group by statement

Categories