I would like if there is a way to fine tune JPA/Hibernate to manage the following use case. I have the following object model:
class Parent {
...
@OneToMany(mappedBy = "definizione", fetch = FetchType.LAZY)
private List<Child> childs;
...
}
class Child {
...
@ManyToOne(fetch = FetchType.LAZY)
private GrandChild grandChild;
...
}
class GrandChild {
...
}
Then I execute the following code:
Parent parent = entityManager.find(id, Parent.class); // (1)
List<Child> childs = parent.getChilds(); // (2)
GrandChild grandChild = null;
for(Child child : childs) {
grandChild = child.getGrandChild(); // (3)
//do somthing with childs
}
What I want to achieve is:
- to tune hibernate on use case basis, i.e. without changing the entity class;
make hibernate perform two queries:
select parent0_.* -- all Parent columns from PARENTS parent0_ where parent0_.ID=? SELECT childs0_.* -- all Child columns grandchild1_.* -- all GrandChild columns FROM childs childs0_ LEFT OUTER JOIN grand_childs grandchild1_ ON childs0_.grand_child_id = grandchild1_.id WHERE childs0_.parent_id =?
The basic behaviour of the snippet above with the Child - GrandChild lazy fetch are:
- (1) one query for the
Parententity is performed; - (2) one query for all
Childentity of theParententity is performed; - (3) n query, one for each the
GrandChildentity.
By reading Hibernate Fetching, I found the following solutions, but none of them achieve what I want:
Change entity class fetch strategy
class Child {
...
@ManyToOne(fetch = FetchType.EAGER)
private GrandChild grandChild;
...
}
PROS: The number of queries performed are the ones desired;
CONS: this solution affects other use cases, for some reasons I don't want to change the fetch strategy at entity class level.
Dynamic fetching via queries
This case is valid both for jpql and Crieria query.
final Parent parent = entityManager.createQuery(
"SELECT p FROM Parent p LEFT JOIN FETCH p.childs c JOIN FETCH c.grandChild WHERE p.id = :id",
Parent.class
)
.setParameter("id", id)
.getSingleResult();
List<Child> childs = parent.getChilds();
GrandChild grandChild = null;
for (Child child : childs) {
grandChild = child.getGrandChild();
//do somthing with childs
}
The executed query is:
SELECT parent0_.*, -- all Parent fields
childs1_.*, -- all Child fields
grandchild2_.* -- all GrandChild fields
FROM parents parent0_
LEFT OUTER JOIN childs childs1_ ON parent0_.id = childs1_.parent_id
LEFT JOIN grand_childs grandchild2_ ON childs1_.grand_child_id = grandchild2_.id
WHERE parent0_.id =?
PROS: Only one query performed.
CONS: A lot of duplicated data are loaded from the db, I don't want to load Parent entity more then once.
Dynamic fetching via JPA entity graph
@Entity
@NamedEntityGraph(
name = "parent.childs.grandchild",
attributeNodes = {
@NamedAttributeNode(value = "childs", subgraph = "childs.grandchild")
},
subgraphs = {
@NamedSubgraph(
name = "childs.grandchild",
attributeNodes = {
@NamedAttributeNode(value = "grandChild")
}
)
}
)
public class Parent extends BaseEntity{
...
}
And the code to load:
final Parent parent = entityManager.find(
Parent.class,
id,
Collections.singletonMap(
"javax.persistence.fetchgraph",
entityManager.getEntityGraph( "parent.childs.grandchild" )
)
);
List<Child> childs = parent.getChilds();
GrandChild grandChild = null;
for (Child child : childs) {
grandChild = child.getGrandChild();
//do somthing with childs
}
The executed query is the same of Dynamic fetching via queries, so pros and cons are the same.