3

当前堆栈:

  • 春季启动 1.5.1
  • Spring Data JPA 1.11.0
  • 休眠核心 5.2.6

假设我们有以下@Entity结构

@Entity
class Root {

    @Id
    private Long id;

    @OneToMany
    @JoinColumn(name = "root_id")
    private Set<Child> children
}

@Entity
class Child {

    @Id
    private Long id;

    @OneToMany
    @JoinColumn(name = "child_id")
    private Set<Grandchild> grandchildren;
}

@Entity
class Grandchild {

    @Id
    private Long id;
}

当我查询所有/特定Root对象时,Hibernate 仅从相应的表中选择,结果对象的childrenSet 是null一个 Hibernate 代理 - 它应该是。

当我调用getChildren()Hibernate 正确初始化集合但也(无根据地)获取每个Child对象的grandchildrenSet。

有人可以准确解释为什么会发生这种递归提取,有没有办法禁用它?


我做了更多的挖掘,这就是我想出的:它似乎与 Hibernate 映射的方式有关,@OneToMany具体取决于目标集合是 aList还是Set.

private final RootRepo repo;

如果集合是Sets

public void test() {
    List<Root> all = repo.findAll(); // SELECT root0_.* FROM root root0_

    all.forEach(root -> {
        System.out.println(root.getChildren() == null); // false
        System.out.println(Hibernate.isInitialized(root.getChildren())); // false

        root.getChildren().forEach(child -> {
            // SELECT child0_.* FROM children child0_
            // SELECT grandchild0_.* FROM grandchildren grandchild0_
            System.out.println(child.getGrandchildren() == null); // false
            System.out.println(Hibernate.isInitialized(child.getGrandchildren())); // true

            child.getGrandChildren().forEach(grandchild -> {});
        });
    });
}

然而,随着Lists

public void test() {
    List<Root> all = repo.findAll(); // SELECT root0_.* FROM root root0_

    all.forEach(root -> {
        System.out.println(root.getChildren() == null); // false
        System.out.println(Hibernate.isInitialized(root.getChildren())); // false

        root.getChildren().forEach(child -> {
            // SELECT child0_.* FROM children child0_
            System.out.println(child.getGrandchildren() == null); // false
            System.out.println(Hibernate.isInitialized(child.getGrandchildren())); // false

            child.getGrandChildren().forEach(grandchild -> {
                // SELECT grandchild0_.* FROM grandchildren grandchild0_
            });
        });
    });
}

我是一个可以证明的白痴。

我正在使用 Lombok 为我的POJO生成 getter/setter 等,其注释的默认实现会@EqualsAndHashCode生成这两种方法,同时考虑到每个字段......包括子集合。

4

1 回答 1

0

我很惊讶 Root 的孩子实际上是空的。

它在您的情况下的工作方式(请仔细检查子项是否实际设置为空),是当您访问 getChildren() 时(例如通过调用 size() )..该集合是从数据库中获取的以及它所有急切的依赖关系。

所有惰性依赖项(在这种特殊情况下为孙子项)都被实例化为代理对象,但不应在数据库上为这些对象执行 sql 查询(请检查)。

此外

它从来没有发生在我身上,但只是要记住的一点点。根据 JPA,延迟加载功能只是对持久性提供程序的一个提示。即使您将 fetchType 设置为 LAZY 或通常您希望默认情况下延迟加载集合依赖项(这可以在配置会话工厂时完成),实现仍可能决定执行 EAGER 提取:

定义从数据库中获取数据的策略。EAGER 策略是对持久性提供程序运行时的要求,即必须急切地获取数据。LAZY 策略是对持久性提供程序运行时的提示,即在首次访问数据时应该延迟获取数据。允许该实现急切地获取已指定 LAZY 策略提示的数据。

于 2017-02-08T15:48:38.323 回答