I have the following classes:
public class A{
private Set<B> bSet;
}
public class B{
private Set<C> cSet;
private boolean isAvailable;
}
public class C{}
The HQL is:
select a from A a
left join fetch a.bSet
left join fetch b.cSet
where a.id = ? and bSet.isAvailable = ?
The question is I can't use set in where clause. How can I process this query?
try this way I just gave alias to your queries part
select a from A a
left join fetch a.bSet b
left join fetch b.cSet c
where a.id = ? and b.isAvailable = ?
if that not work for you try toreplace ? with :id and :isAvilable Respectively as folows
select a from A a
left join fetch a.bSet b
left join fetch b.cSet c
where a.id = :id and b.isAvailable = :isAvailable
most likely one of the two must work for you.
Related
I have a similar db structure. The only difference that I have more tables on the path from A to C entity:
And I have following mapping for this structure:
#Entity
#Table(name = "a")
class A {
#Id
private int id;
private String title;
#ElementCollection(targetClass=String.class)
#Formula("(select (c.useful_information) from A a " +
"join B b on a.id = b.a_id " +
"join C c on b.id = c.b_id " +
"where a.id = id)")
private List<String> usefulStuff;
}
My aim is to get a list of all useful things from table C in entity A.
But I get syntax errors.
Could you say what's wrong in my example? And maybe you know better way for this purpose?
The problem in your #Formula annotation is the "from A a". The value passed to this annotation is actually SQL, not JPQL.
Thus, if you want to alias the table to reference it elsewhere, you need to rather write FROM A AS a.
On my project I'm using Groovy with Spring Data JPA's Specification's to construct Hibernate queries.
I can't provide my actual queries but to illustrate my problem let's say I have Building entities, and each Building has Floors and each Floor has both Rooms and Windows.
The behavior I'm attempting to simulate is something like this native SQL query:
SELECT b.*, r.*
FROM building b
INNER JOIN floor f ON b.id = f.building_id
INNER JOIN window w ON f.id = w.floor_id
LEFT OUTER JOIN room r ON f.id = r.floor_id
WHERE w.id = 1;
I have a specification similar to the below:
public class MySpec implements Specification<Building> {
#Override
public Predicate toPredicate(final Root<Building> root, final CriteriaQuery<?> query, final CriteriaBuilder cb) {
final Join floorsJoin = root.join("floors");
final Join windowsJoin = floorsJoin.join("windows");
//I'd like to remove this line
final Fetch floorsFetch = root.fetch("floors"); // <---
floorsFetch.fetch("rooms", JoinType.LEFT);
cb.equal(windowsJoin.get("id"), 1L);
}
}
The line annotated above is my issue. If I leave it, the generated query looks something like this:
SELECT b.*, f2.*, r.*
FROM building b
INNER JOIN floor f ON b.id = f.building_id
INNER JOIN window w ON f.id = w.floor_id
INNER JOIN floor f2 ON b.id = f2.building_id
LEFT OUTER JOIN room r ON f2.id = r.floor_id
WHERE w.id = 1;
(notice the duplicate INNER JOIN of floor and the unneeded f2.* data)
If I remove it, and use the floorsJoin instead to fetch rooms, I get the following Hibernate error:
org.hibernate.QueryException: query specified join fetching, but the owner of the fetched association was not present in the select list
The unneeded f2.* data would be OK except I can't replace the above floorsJoin with the floorsFetch because I need to join with the windows table (without fetching windows) and the Fetch class doesn't have a .join method.
I'm having a difficult time figuring out how I would accomplish what I need while still generating a single query; surely I must be missing something simple.
Any thoughts or advice you could provide would be much appreciated.
Thanks a lot,
B.J.
Well it's not that simple with the JPA Criteria API. With Hibernate you could simply cast the Fetch to a Join I guess but that's not going to help you that much. I am not sure how you use the specification in this case, but if you could write the query as a whole it could look like the following
CriteriaBuilder cb = entityManager.getCriteriaBuilder();
CriteriaQuery<Tuple> cq = cb.createTupleQuery();
Root<Building> root = cq.from(Building.class);
final Join floorsJoin = root.join("floors");
final Join windowsJoin = floorsJoin.join("windows");
final Join roomsJoin = floorsJoin.join("rooms", JoinType.LEFT);
cb.equal(windowsJoin.get("id"), 1L);
cq.multiselect(
root,
roomsJoin
);
I have 5 entities :
#Entity
public class A {
#OneToMany(mappedBy="a", fetch=FetchType.EAGER)
#Fetch(FetchMode.JOIN)
private Set<B1> b1s;
#OneToMany(mappedBy="a", fetch=FetchType.EAGER)
#Fetch(FetchMode.JOIN)
private Set<B2> b2s;
}
#Entity
public class B1 {
#ManyToOne
#JoinColumn(name="c")
private C c;
}
#Entity
public class B2 {
#ManyToOne
#JoinColumn(name="c")
private C c;
}
#Entity
public class C {
#OneToMany(mappedBy="d", fetch=FetchType.EAGER)
#Fetch(FetchMode.JOIN)
private Set<D> ds;
}
#Entity
public classD {
}
So in short,
A eager join two set of Entity B1 and B2, each one eager joining (implicitely) an Entity C which in turn eager join a set of Entity D.
when loading an A object, the generated query look like
select (...) from A a0
left outer join B1 b1 on a0.id=b1.aid
left outer join C c1 on b1.cid=c1.id
left outer join D d1 on c1.id=d1.cid
left outer join B2 b2 on a0.id=b2.aid
left outer join C c2 on b2.cid=c2.id
where a0.id=?
The problem is that the Set of Entity D wich are linked to c2 (C Entity loaded via B2 Entity) are not loaded in the same query, resulting in N subsequent queries for each c2 object.
I would expect Hibernate to generate another left outer join for these objects in the first query, as it already does for the first occurence of D.
I am missing something ? I use hibernate 3.6 on an Oracle DB, is it an known issue ?
Thank you for your time.
Indeed, this seems to be a bug or an incomplete feature. I was able to reproduce it in a simpler scenario as well:
#Entity
public class A {
#OneToMany(fetch=FetchType.EAGER)
#Fetch(FetchMode.JOIN)
#JoinColumn
private Set<B> bs1;
#OneToMany(fetch=FetchType.EAGER)
#Fetch(FetchMode.JOIN)
#JoinColumn
private Set<B> bs2;
}
#Entity
public class B {
#OneToMany(fetch=FetchType.EAGER)
#Fetch(FetchMode.JOIN)
#JoinColumn
private Set<C> cs;
}
#Entity
public class C {
}
When loading A by id, the following joins are generated:
from
A a0_
left outer join
B bs1x1_
on a0_.id=bs1x1_.bs1_id
left outer join
C cs2_
on bs1x1_.id=cs2_.cs_id
left outer join
B bs2x3_
on a0_.id=bs2x3_.bs2_id
So, if you really want to fetch everything in one query, you'll have to do it with HQL:
select a from A a
left join fetch a.b1s b1
left join fetch b1.c c1
left join fetch c1.ds
left join fetch a.b2s b2
left join fetch b2.c c2
left join fetch c2.ds
where a.id = ?
However, I assume that the collections being joined are really small. Because these kinds of joins produce full Cartesian product and are considered a very bad anti-pattern.
Suppose that each of the collections contains only 100 rows (A has 100 b1s and 100 b2s, and each of b1s and b2s has a C which has 100 ds). Then you will get and read a result set containing 100 million rows!
But with lazy collections and batch fetching for example, you would read a total of about 400 rows in a few queries, which is much faster than reading 100 million rows in one query.
I have two entities, whith OneToMany relationship:
class Parent {
int parentId;
Set<Children> children;
}
class Child {
int childId;
}
and the following HQL query:
"SELECT p.children FROM Parent p left join p.children as c WHERE p.id=:pid AND c.id:=cid"
is returning me all children of the parent with given id, while I would expect only a child with id matching given child id. What am I doing wrong here?
You need to use inner join instead of left join.
Try either of the below, not sure of exact syntax.
SELECT p.children FROM Parent p inner join p.children as c WHERE p.id=:pid AND c.id:=cid
or
SELECT p.children FROM Parent p, p.children as c WHERE p.id=:pid AND c.id:=cid
I am trying to "extract" Embeddable classes based on some criterias from a list in an Entity. Either with the help of JPQL or Criteria API. I am not a pro at this, so please help me out. Been googling for 4 hours solid for an answer without any results.
These are the classes:
#Entity
public class PostOffice {
#Id
private Long id;
#ElementCollection(fetch=FetchType.LAZY)
#CollectionTable(joinColumns=#JoinColumn(name = "CARRIERID"))
private List<PostalCarrier> carriers;
}
#Embeddable
public class PostalCarrier {
#JoinColumn(name = "area")
private Area area;
}
#Entity
public class Area {
#Id
private int code;
}
So, basically what I'm trying to achieve is something like this.
TypedQuery<PostalCarrier> query = entityManager.createQuery("SELECT p.carriers FROM PostOffice p
WHERE p.id = ?1 AND p.carriers.area.code = ?2", PostalCarrier.class);
query.setParameter(1, postOfficeId);
query.setParameter(2, areaCode);
I only want to get a list of the PostalCarriers at specific area code from a specific PostOffice.
Any help is much appreciated! :)
I think I'm almost there, but keep getting the following error:
Error compiling the query [SELECT h FROM PostOffice p INNER JOIN p.carriers h
WHERE p.id = ?1 AND h.area.code = ?2], line 1, column 71: unknown state or
association field [area] of class [com.test.PostalCarrier].
You must make a join to the PostalCarrier. You can't access a property from a collection. Thus, postalcarrier.area isn't correct.
select postalcarrier from PostOffice p
inner join p.carriers postalcarrier
where p.id = :postOfficeId
and postalcarrier.area.code = :areaCode