【问题标题】:JPA/Hibernate load lazy child entity with fetching grand-child entityJPA/Hibernate 通过获取孙子实体加载惰性子实体
【发布时间】:2018-01-25 17:02:45
【问题描述】:

我希望有办法微调 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. 根据用例调整休眠,即不更改实体类;
  2. 让休眠执行两个查询:

    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 =?
    

上面的sn-p与Child-GrandChildlazy fetch的基本行为是:

  • (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
    }   

执行的查询与通过查询动态获取相同,因此优缺点相同。

【问题讨论】:

  • @JBNizet,我阅读了文档,但我没有找到一种方法来做我只会在子孙子协会上做的事情,所以我发布了这个问题,询问是否有可能,或者这样做的唯一方法是更改​​获取策略
  • 我已阅读您的更新,关于通过查询动态获取 Hibernate 不会多次加载您的父对象,因此您最终会加载一个父对象。您无法在单个查询中实现您想要的。

标签: hibernate jpa


【解决方案1】:

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-11-16
    • 2015-11-22
    相关资源
    最近更新 更多