JPA 2 Criteria - group by and count - java

I have two entities, ShoppingCart and ShoppingCartLine. ShoppingCart has a collection of ShoppingCartLines. I am trying to create a JPA query using criteria to get a list of ShoppingCart ids and the number of ShoppingCartLines each ShoppingCart has.
Here is the mapping in the ShoppingCart class:
#OneToMany(mappedBy = "shoppingCart", fetch = FetchType.EAGER, cascade = CascadeType.ALL, orphanRemoval=true)
private Set<ShoppingCartLine> shoppingCartLines = new TreeSet<ShoppingCartLine>();
Here is the code I am attempting to create my JPA query with:
CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery<Tuple> cq = cb.createTupleQuery();
Root<ShoppingCart> carts = cq.from( ShoppingCart.class );
Join<ShoppingCart, ShoppingCartLine> lines = carts.join( "shoppingCartLines", JoinType.LEFT);
cq.multiselect( carts.<Long>get( "id" ), cb.count( lines ));
cq.groupBy( carts.<Long>get("id") );
Query tq = em.createQuery( cq );
return tq.getResultList();
When I run this I get an SQLGrammerException, the SQL produced does not look correct to me.
select
shoppingca0_.id as col_0_0_,
count(.) as col_1_0_
from
SHOPPINGCART shoppingca0_
left outer join
SHOPPINGCARTLINE shoppingca1_
on shoppingca0_.id=shoppingca1_.shoppingCart_id,
SHOPPINGCARTLINE shoppingca2_
where
shoppingca0_.id=shoppingca2_.shoppingCart_id
group by
shoppingca0_.id
I should mention I am using Hibernate 3.5.4 with MySQL5Dialect
This is the query I am wanting to generate:
select
sc.id,
count(scl.id)
from
shoppingcart sc
left join
shoppingcartline scl
on
scl.shoppingCart_id = sc.id
group by
sc.id
Any idea what I'm doing wrong? Thanks.

Try to replace join and multiselect lines with
SetJoin<ShoppingCart, ShoppingCartLine> lines =
carts.joinSet( "shoppingCartLines", JoinType.LEFT);
cq.multiselect( carts.<Long>get( "id" ), cb.count( lines.<Long>get( "id" ) ));

Related

Order list by max date of a children (using Hibernate Critéria)

I'm trying to sort a list by the max date of a children class, using hibernate criteria:
Domain:
class Entity {
List<Children> childrens;
}
class Children {
Date date
}
Something like this in SQL:
SELECT
*
FROM
entity
INNER JOIN
children c1 ON c1.entity_id = entity.id
WHERE
c1.date =
(SELECT MAX(c2.date) FROM children c2 WHERE c2.entity_id = c1.entity_id)
ORDER BY
c1.date DESC
Does anyone know how to do this?
You can formulate something like that with HQL or the JPA Criteria API. Here is the HQL version:
from
Entity e
order by
(select max(c.date) from e.children c) desc
or with the Criteria API:
CriteriaBuiler cb = em.getCriteriaBuilder();
CriteriaQuery<Entity> q = cb.createQuery(Entity.class);
Root<Entity> r = q.from(Entity.class);
Subquery<Date> s = q.subquery(Date.class);
Join<?, ?> c = s.correlate(r).join("children");
s.select(cb.max(c.get("date")));
q.orderBy(cb.desc(s));
List<Entity> list = em.createQuery(q).getResultList();

Jpa criteria api - create class that connect other class

Let's say I have the following SQL:
select * from table1
inner join table2 on table1.id = table2.table1id;
inner join table3 on table2.id = table3.table2id;
inner join table4 on table3.id = table4.table3id;
and I have Java entities like table1, table2, table3 and table4.
I want to map this query using criteria API. In order to do that I created class Table5 which contains all of fields of all classes.
Then I created a repository with the method:
public List<Table5> getAllTable5 () {
CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery<Table5> query = cb.createQuery(Table5.class);
Root<Table1> root = query.from(Table1.class);
Join<Table1, Table2> table1Table2Join= root.join(Table1_.TABLE2);
Join<Table2, Table3> table2Table3Join= root.join(Table2_.TABLE3);
Join<Table3, Table4> table3Table4Join= root.join(Table3_.TABLE4);
query.multiselect(
root.get // all fields
);
TypedQuery<Table5> typedQuery = em.createQuery(query);
return typedQuery.getResultList();
}
Is it possible to create class like:
class Table5 {
private Table1 table1;
private Table2 table2;
private Table3 table3;
private Table4 table4;
// getters setters constructors
}
If yes, how should getAllTable5 method look like?
You can either select every table entity, or select just the first one and rely on join fetching.
public List<Table5> getAllTable5 () {
CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery<Table1> query = cb.createQuery(Table1.class);
Root<Table1> root = query.from(Table1.class);
query.select(root);
TypedQuery<Table1> typedQuery = em.createQuery(query);
EntityGraph entityGraph = em.createEntityGraph();
// Apply subgraphs for the associations to fetch
typedQuery.setHint("javax.persistence.loadgraph", entityGraph);
List<Table1> list = typedQuery.getResultList();
List<Table5> table5s = new ArrayList<>(list.size());
for (Table1 t : list) {
table5s.add(new Table5(t, t.getTable2(), , t.getTable2().getTable3(), , t.getTable2().getTable3().getTable4());
}
return table5s;
}

JPA Criteria query double joins and hibernate error with-clause

Based on this answer https://stackoverflow.com/a/2111420/3989524 I first created a working SQL:
SELECT d.*
FROM device d
LEFT OUTER JOIN installation_record ir1 ON (d.id = ir1.device)
LEFT OUTER JOIN installation_record ir2 ON (d.id = ir2.device AND ir1.install_date < ir2.install_date)
WHERE ir2.id IS NULL AND ir1.uninstall_date IS NULL;
and then a criteria query which looks to produce an equivalent HQL (in the end), but Hibernate throws an error:
org.hibernate.hql.internal.ast.QuerySyntaxException: with-clause referenced two different from-clause elements
The HQL from the error message:
SELECT generatedAlias0 FROM Device AS generatedAlias0
LEFT JOIN generatedAlias0.installationRecordList AS generatedAlias1
LEFT JOIN generatedAlias0.installationRecordList AS generatedAlias2
WITH generatedAlias1.installDate<generatedAlias2.installDate
WHERE ( generatedAlias2.id IS NULL ) AND ( generatedAlias1.uninstallDate IS NULL )
The criteria query:
final CriteriaBuilder cb = em.getCriteriaBuilder();
final CriteriaQuery<Device> cq = cb.createQuery(Device.class);
final Root<Device> root = cq.from(Device.class);
final Join<Device, InstallationRecord> join1 = root.join(Device_.installationRecordList, JoinType.LEFT);
final Join<Device, InstallationRecord> join2 = root.join(Device_.installationRecordList, JoinType.LEFT);
join2.on(cb.lessThan(join1.get(InstallationRecord_.installDate), join2.get(InstallationRecord_.installDate)));
cq.select(root);
final List<Predicate> predicates = Lists.newArrayList();
predicates.add(cb.isNull(join2.get(InstallationRecord_.id)));
predicates.add(cb.isNull(join1.get(InstallationRecord_.uninstallDate)));
cq.where(predicates.toArray(new Predicate[] {}));
return em.createQuery(cq).getResultList();
Is there anyway to get what I want or there no other way around some internal hibernate bug.
Maybe:
final Join<InstallationRecord, InstallationRecord> join2 = root.join(InstallationRecord_.id, JoinType.LEFT);
because ON (d.id = ir2.device AND is missing in the HQL.

Why is CriteriaBuilder creating a malformed query for "in" CollectionTable?

When attempting to generate dynamic queries using CriteriaBuilder, Hibernate is not creating the proper SQL with regards to an Entities member variable associated with #ElementCollection.
Sample Entity:
#Entity
#Table(name = "myclass")
public class MyClass {
...
#ElementCollection
#CollectionTable(
name = "myclass_mysubclass",
joinColumns = #JoinColumn(name = "myclass_id")
)
#Column(name = "mysubclass_id")
private List<Integer> mySubClassIDs;
...
}
CriteriaBuilder code:
CriteriaBuilder criteriaBuilder = getEntityManager().getCriteriaBuilder();
CriteriaQuery criteriaQuery = criteriaBuilder.createQuery(MyClass.class);
Root<T> root = criteriaQuery.from(MyClass.class);
Expression<Object> e = root.get("mySubClassIDs");
List<Object> o = (List<Object>) entry.getValue();
criteriaQuery.where(e.in(o));
where entry.getValue() will return an ArrayList<Integer> of [1]
Produces:
SELECT distinct count(myclass0_.id) as col_0_0_
FROM hotel myclass0_
cross join myclass_mysubclass mySubClassids1_
where myclass0_.id=mySubClassids1_.myclass_id and (. in (1))
Why is Hibernate not generating the "in" clause properly? the "." should be mySubClassids1_.mysubclass_id
Am I missing something in the annotation of the member variable? Doesn't seem so, as it is enough to generate the cross join.
The env is Jboss AS 7 with Hibernate 4.2.7.SP1-redhat-3 on jdk-6
Your schema is creating two separate tables:
create table myclass (
id int8 not null,
primary key (id)
);
create table myclass_mysubclass (
myclass_id int8 not null,
mysubclass_id int4
);
So, it seems you need to do a join instead of a get:
Expression<Object> e = root.join("mySubClassIDs");
Worked for me at any rate.

Unneccessary join in jpa subquery

I have following jpa criteria query:
CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery<Company> cq = cb.createQuery(Company.class);
Root<Company> root = cq.from(Company.class);
cq.select(root);
Subquery<String> sq = cq.subquery(String.class);
Root<Employee> subroot = sq.from(Employee.class);
sq.select(subroot.get(Employee_.lastName));
Predicate typePredicate = cb.equal(subroot.get(Employee_.lastName), "Doe");
Predicate correlatePredicate = cb.equal(root.get(Company_.employees), subroot);
sq.where(cb.and(typePredicate, correlatePredicate));
cq.where(cb.exists(sq));
TypedQuery<Company> typedQuery = em.createQuery(cq);
List<Company> companies = typedQuery.getResultList();
Eclipselink produces following SQL:
SELECT t0.ID, ... FROM COMPANY t0
WHERE EXISTS (SELECT t1.LASTNAME FROM EMPLOYEES t2, EMPLOYEES t1
WHERE (((t1.LASTNAME = ?) AND (t1.ID = t2.ID))
AND (t2.COMPANY_ID = t0.ID)))
As you can see there is an unneccessary join on table EMPLOYEES. How do I get rid of this join?
You don't seem to need a subquery for the query, just a join should be enough,
http://en.wikibooks.org/wiki/Java_Persistence/Criteria#Join
Otherwise, what version are you using? Can you try EclipseLink 2.4.
If it still has the duplicate, please log a bug and vote for it.
You might be able to use the inverse ManyToOne, instead of the OneToMany (i.e. root == subroot.get("company") ).
Also try JPQL in 2.4, is the join optimized?

Categories