我想如果有一种方法可以微调JPA / Hibernate来管理以下用例。 我有以下对象模型:
class Parent {
...
@OneToMany(mappedBy = "definizione", fetch = FetchType.LAZY)
private List<Child> childs;
...
}
class Child {
...
@ManyToOne(fetch = FetchType.LAZY)
private GrandChild grandChild;
...
}
class GrandChild {
...
}
然后我执行以下代码:
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
}
我想要达到的目标是:
让hibernate执行两个查询:
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 =?
上面使用Child - GrandChild懒惰提取的代码段的基本行为是:
Parent
实体的一个查询; Child
实体的所有Parent
实体的一个查询; GrandChild
实体一个。 通过阅读Hibernate Fetching,我找到了以下解决方案,但没有一个达到我想要的目的:
class Child {
...
@ManyToOne(fetch = FetchType.EAGER)
private GrandChild grandChild;
...
}
PROS :执行的查询数量是所需的数量;
缺点:此解决方案影响其他用例,由于某些原因我不想更改实体类级别的抓取策略。
这种情况对jpql和Crieria查询都有效。
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
}
执行的查询是:
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 :只执行了一次查询 CONS :从db加载了大量重复数据,我不想再加载一次Parent实体。
@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{
...
}
要加载的代码:
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
}
执行的查询与通过查询动态提取相同,因此优缺点相同。
答案 0 :(得分:2)
您可以使用JOIN FETCH执行查询并一次性获取所有内容。
String hql = "SELECT parent FROM Parent parent " +
"LEFT JOIN FETCH parent.child child " +
"JOIN FETCH child.grandChild " +
"WHERE parent.id = :parentId";
Parent parent = entityManager.createQuery(hql, Parent.class).getSingleResult()
.setInteger("parentId", parentId);
List<Child> childs = parent.getChilds();
for(Child child : childs) {
GrandChild grandChild = child.getGrandChild();
//more code...
}
查看此文档:https://docs.jboss.org/hibernate/orm/5.1/userguide/html_single/chapters/fetching/Fetching.html