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
Related
I am using queryDsl-Sql I need to do an update statement with a join clause like:
update MY_TABLE t1 set t1.MY_FIELD = 'SOME_VALUE'
JOIN MY_TABLE_2 t2 ON t1.FIELD_WITH_FK = t2.ID
where t2.OTHER_FIELD=12324556789 AND t2.OTHER_FIELD like '%something%'
Unfortunately, on queryDsl I was only able to do subqueries during update but not joins:
dsl.update(table1)
.set(my_field, "SOME_VALUE")
.where(
field_with_fk.in(
dsl.select(id).from(table2).where(other_field.eq(12324556789))
, fieldName.like("%something%"));
Which is translated to a subquery (as expected) and happens to take much much more time (difference between 10 seconds using joins and more than 1h using subselects)
I have seen that on JPAUpdateClause is possible to do so but I am not using QueryDsl-jpa here. QueryDSL-SQL has only SqlUpdateClause and I was not able to find how to join different tables when using It.
I think this is more a workaround than a solution, but maybe helps
also others:
dsl.update(table1)
.set(field1, myValue)
.addFlag(
QueryFlag.Position.START_OVERRIDE, //it replaces update by update with join
Expressions.stringTemplate("update {0} join {1} on {2} = {3} #", // I need # to ignore table1 otherwise incorrect SQL
table1, table2, table1FieldWithFkToTable2, table2Id))
.where(other_field.eq(12324556789), fieldName.like("%something%"));
this generates the following SQL:
update table1 join table2 on table1FieldWithFkToTable2 = table2Id #table1
set field1 = myValue
where other_field=12324556789 and fieldName like '%something%'
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)
I currently have a query like this:
SELECT DISTINCT t.column1, SUM(t2.column2 IS NOT NULL)
FROM table t
LEFT OUTER JOIN table t2 on table.id = t2.id
GROUP BY column1, column2;
I am trying to implement the query using Spring JPA CriteriaBuilder. I see the CriteriaBuilder.sum() method, but I don't see a way to apply the IS NOT NULL part to the selection. Column2's data type is string.
Sample of my code
criteriaBuilder.multiselect(root.get("column1"), cb.sum(root.get("column2")));
Only in MySQL would such a query run, due MySQL’s relaxed syntax rules.
In particular, in mysql sum(column2 is not null) is a count, not a sum. The expression column2 is not null is boolean and in mysql false is 0 and true is 1, so summing this expression is a mysql hack to count the number of times column2 is not null.
To convert it to standard sql:
select
t.column1,
count(t2.column2)
from table t
left join table t2 on t.id = t2.id
group by t.column1
This works because count() (and all aggregate functions) ignore nulls.
This version also corrects the errant column in the group by clause - in any other database, your query would have produced a “grouping by aggregate expression” error.
This query will produce the same result in MySQL as your current query.
I was able to find a solution to my problem. Thanks to #bohemian for helping me write a correct sum expression.
final CriteriaBuilder cb = em.getCriteriaBuilder();
final CriteriaQuery<Model1> cq = cb.createQuery(Model1.class);
final Root<Model1> root = cq.from(Model1.class);
final Join<Model1, Model1> selfJoin =
root.join("tableJoinColumn", JoinType.LEFT);
selfJoin.on(...);
cq.multiselect(root.get("column1"), cb.sum(cb.selectCase()
.when(cb.isNull(selfJoin.get("column2")), 0).otherwise(1).as(Long.class)));
...
The self join required me to create an additional property on my model.
Model1.java
/**
* Property for LEFT INNER JOIN.
*/
#OneToOne(fetch = FetchType.LAZY)
#JoinColumn(name="id")
private Model1 tableJoinColumn;
How to use JPA CriteriaBuilder selectCase() so that it can have Predicate as result?
Self join in criteria query
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")));
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.