JPA left/right join query - java

I have this structure from entities Aa, Bb, Cc:
Aa has a list of Bb
Bb has a list of Cc
-
public class Aa{
#OneToMany
List<Bb> listBb;
}
public class Bb{
#OneToMany
List<Bb> listCc;
}
I would like to create a JPA Criteria API query to pool Aa by an id of C:
public A getAaByCcId(long id) {...}
In native sql I would have try left join (twice). How do I do this using JPA?

You also do it with joins in JPQL:
select a from Aa a
inner join a.listBb b
inner join b.listCc c
where c.id = :cId
Note that inner joins can be used here, since you have a restriction on c.id = :cId, which can only be true if B and C exist. But you could use left joins as well.
EDIT:
Using a Criteria query, it would look like the following (not tested):
CriteriaBuilder builder = em.getCriteriaBuilder();
CriteriaQuery<Aa> criteria = builder.createQuery(Aa.class);
Root<Aa> a = criteria.from(Aa.class);
CollectionJoin<Aa, Bb> b = a.join(Aa_.listBb);
CollectionJoin<Bb, Cc> c = b.join(Bb_.listCc);
criteria.where(builder.equal(c.get(Cc_.id), cId));
return em.createQuery(criteria).getResultList();

Assuming that each entity has a reference to its parent entity, where Cc's reference to a Bb is called bb and Bb's reference to an Aa is called aa, then in JPQL, you can do:
select cc.bb.aa from Cc cc where cc.id = ?
The criteria version of this should be a simple translation.

Related

Update Entity Relationship via UPDATE .. SET Query

I have two entities:
class A {
#OneToMany(mappedBy = "a")
List<B> bs;
// getter/ setter
}
class B {
#ManyToOne
A a;
// getter/ setter
}
To delete one b, I first need to invalidate that relationship.
"Traditionally" I would do something like that:
A a = em.getReference(A.class, entityIdA)
B b = em.getReference(B.class, entityIdB);
a.getBs().remove(b);
b.setA(null);
em.remove(b);
This is not very performant, if the List of a's is getting large (a few hundreds in my case).
I know I can also use JPQL to create an update query.
something like this:
Query q = em.createQuery("UPDATE B b SET b.a = NULL");
q.executeUpdate();
Question: What would be the corresponding JPQL query to remove one b from a's list of bs?
In short:
How to translate
a.getBs().remove(b);
into a JPQL query?
EDIT: the mentioned update query translates to
UPDATE B SET A_ID = ? WHERE (ID = ?)
bind => [null, 2]
Tables look like this:
A
ID
B
ID A_ID
From the comments and from this question, changing the owning side of the relationship is sufficient.
Therefore, to do
a.getBs().remove(b);
as an jpql query, one can do
"UPDATE B b SET b.a = NULL"
This will release the bidirectional relationship between a and b.
Note that you might need to clear the L2 cache or close the EntitiyManagerFactory for this to take effect.
factory.getCache().evictAll();

With JPA Criteria, how would I Fetch a child entity of a Joined entity without Fetching the Joined entity?

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
);

JPA query with two left join

I have three classes: A.java, B.java and C.java. A and B extend C. How do I write a JPA query to works correctly? I tried
Query query = em.createQuery("select c from C c left join A a left join B b where (c.id = a.id or c.id = b.id)");
The short answer is that you don't. In JPA:
The default strategy, InheritanceType.SINGLE_TABLE, is used if the #Inheritance annotation is not specified on the root class of the entity hierarchy
When you select everything from table C, the JPA provider will instantiate classes based on the discriminator column, so
List<C> rl = em.createQuery("select c from C c", C.class).getResultList();
System.out.println(rl);
will give you:
Hibernate: select c0_.id as id2_0_, c0_.DTYPE as DTYPE1_0_ from C c0
[model.C#2cac4385, model.A#6731787b, model.B#16f7b4af]
If you need to you can use the instanceof operator in Java to determine the result types. If you want a specific subclass, then just query it:
List<A> rl = em.createQuery("select a from A a", A.class).getResultList();
System.out.println(rl);
which will give you:
Hibernate: select a0_.id as id2_0_ from C a0_ where a0_.DTYPE='A'
[model.A#3fc9dfc5]
Reference: Entity Inheritance

Hibernate java getting a complete object

I want to get the complete A object from database, this include the B object that contains the C and D.
I have A B C D classes
class A
private B object
Class B
private List<C> objects
Class C
private List<D> objects
This is what I do:
Session session = sf.openSession();
String consulta = "select a from A a;
Query q = session.createQuery(consulta);
List<A> aaaa= q.list();
This is getting the A with B, but B is not containing the list of C.
Thanks for helping
Use JOIN FETCH in your query to get everything in one go:
Query q = "select b from B b join fetch b.c";
This addresses the N+1 problem that you would have if you initialized everything lazily.

Hibernate Query Criteria for mappings involving inheritance

Lets say that class 'X' is mapped to table 'X' class 'A' is mapped to Table 'A' and Class 'B is mapped to table 'B'.
Table X Structure:(X_ID, some other columns
Table A Structure:(A_Id,X_Id, some other columns)
Table B Structure:(A_Id, some other columns)...Table B also has A_Id
Class 'B' extends class 'A'. We have the mapping files for both of them as:
Class 'A' Parent Mapping file:
#Entity
#Table(name = 'A')
#Inheritance(stratergy=InheritanceType.Joined)
public abstract class A {
#Id #Clumns(name = "A_Id)
#GeneratedValue
protected Long aId;
-- some more A specific fields
}
Class 'B' Mapping file:
#Entity
#Table(name= 'B')
Public class B extends A{
---- B specific fields
}
Now, I have a SQL Query as below that I need to write using hibernate criteria API.
select * from X
INNER JOIN A
ON X.id = A.id
INNER JOIN B
ON A.id = B.id
where B.name = 'XYZ'
and B.Sex = 'M'
I have come up with:
Criteria c = session.createCriteria(x.class, "x");
.createAlias("x.a", "a")
.createAlias("a.b", "b")
.add(Restrictions.eq("b.sex", "M"))
.add(Restrictions.eq("b.name", "XYZ"));
But, if we check the mapping file, there is no direct reference of B in A. Hence hibernate throws out "B not related to A" entity.
Is there any way this inheritance can be mapped in query crteria
You shouldn't need to reference A at all in your criteria, or use any aliases.
Criteria c = session.createCriteria(B.class);
.add(Restrictions.eq("sex", "M"))
.add(Restrictions.eq("name", "XYZ"));
will give you the result you need.
Because of the InheritanceType.Joined, this will probably produce SQL that includes a join to the the A table (something close to the sql you show), but it isn't necessary to specify that join in the criteria.
The things that look like columns in the criteria are actually (reflective) references to fields in your Java objects. Hibernate figures out the columns to put in the sql from your annotations, and should the join to the A table if it's needed based on the inheritance annotation.
To be sure of this in your context, and to understand all this a bit better, I'd advise trying it and turning on logging of the generated sql as described in this answer to another SO hibernate question.
Try this way:
Criteria rootCrit = session.createCriteria(A.class);
rootCrit.createAlias("B", "B");
rootCrit.add(Restrictions.eq("B.sex", "M"));
rootCrit.add(Restrictions.eq("B.name", "XYZ"));

Categories

Resources