当前位置: 首页 > 知识库问答 >
问题:

JPA/Hibernate加载惰性子实体和获取grand child实体

太叔景曜
2023-03-14

我想知道是否有办法微调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,即不更改实体类;
  • 使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-Sunder lazy fetch的基本行为是:

    • (1)执行对实体的一个查询;
    • (2)对实体的所有实体执行一个查询;
    • (3)n个查询,每个GreenChild实体一个。

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

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

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

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

    优点:只执行了一个查询。
    CONS:从db加载了大量重复数据,我不想多次加载父实体。

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

    执行的查询与通过查询进行的动态抓取相同,因此利弊相同。

  • 共有1个答案

    陆城
    2023-03-14

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

     类似资料:
    • 这组订单应该是惰性加载的,但我得到以下异常:org.springframework.web.util.NestedServletException:请求处理失败;嵌套异常是org.hibernate.lazyInitializationException:未能懒洋洋地初始化Role:...,没有会话或会话被关闭 根本原因:org.hibernate.lazyInitializationExcept

    • 问题内容: 但是,首选解决方案(属性访问)在我的情况下不起作用(我缺少列异常-为什么?) 该模型如下所示:实体和。表含有列是的表,以便它是典型的关系。 现在的问题是,如果我取的实体,我需要有机会获得价值(亦称的实体),而不取实体。我怎样才能做到这一点? 我使用的映射如下所示: 我想做的是调用而无需从DB中额外获取实体。 根据我上面提到的答案,如果我将注释从字段移到getter(在实体上,我对吗?)

    • 考虑以下实体: 的应该被延迟获取。问题是,当使用

    • 我试图用angularjs和SpringMVC构建一个应用程序。我有两个类Privance和Comunidad。: 在我的控制器中: 有人能帮我举个简单的例子吗?

    • 问题内容: 我有两个分别以双向一对多关系存在的实体类 A 和 B。 A.java: B.java 在一个简单的控制台应用程序中,我从数据库中获取了特定的 A 行,并尝试删除其详细信息 B 行(随机),但是 JPA / Hibernate* 不仅删除了该行-甚至没有向该行发出任何 DELETE 语句。数据库。删除 B 行的唯一方法是从 A.java 的集合( LinkedHashSet )中删除相应

    • 我的理解是,默认情况下,Hibernate将所有关系类型的FetchType设置为lazy。 在我的例子中,我有一个双向的OneToMany-ManyToOne关系,如下所示: 另一个问题是我如何得到一个有特定名字的孩子?是否可以通过存储库进行操作?还是需要getChildren(parentId)并迭代直到找到以特定方式命名的那个? 编辑:在一些建议之后,我继续这样实现了我的ChildRepos