gpt4 book ai didi

hibernate - JPA/Hibernate 加载惰性子实体并获取孙实体

转载 作者:行者123 更新时间:2023-12-04 16:06:43 25 4
gpt4 key购买 nike

我想是否有一种方法可以微调 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
}

我想实现的是:

  1. 根据用例调整 hibernate ,即不更改实体类;
  2. 让 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 延迟获取的代码片段的基本行为是:

  • (1) 执行一次针对Parent 实体的查询;
  • (2) 对Parent实体的所有Child实体执行一次查询;
  • (3) n 个查询,每个GrandChild 实体一个。

通过阅读Hibernate Fetching ,我找到了以下解决方案,但都没有达到我想要的效果:

更改实体类获取策略

class Child {
...
@ManyToOne(fetch = FetchType.EAGER)
private GrandChild grandChild;
...
}

优点:执行的查询次数符合要求;
缺点:此解决方案会影响其他用例,出于某些原因我不想在实体类级别更改获取策略。

通过查询动态获取

这种情况对 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 =?

优点:只执行了一个查询。
缺点:从数据库中加载了很多重复的数据,我不想多次加载父实体。

通过 JPA 实体图动态获取

@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
}

执行的查询与通过查询动态抓取相同,所以优缺点相同。

最佳答案

您可以使用 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

关于hibernate - JPA/Hibernate 加载惰性子实体并获取孙实体,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/48448307/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com