Java 禁用休眠';s递归提取

Java 禁用休眠';s递归提取,java,spring,hibernate,jpa,fetching-strategy,Java,Spring,Hibernate,Jpa,Fetching Strategy,当前堆栈: 弹簧靴1.5.1 Spring数据JPA1.11.0 Hibernate核心5.2.6 假设我们有以下@实体结构 @Entity class Root { @Id private Long id; @OneToMany @JoinColumn(name = "root_id") private Set<Child> children } @Entity class Child { @Id private

当前堆栈:

  • 弹簧靴1.5.1
  • Spring数据JPA1.11.0
  • Hibernate核心5.2.6
假设我们有以下
@实体
结构

@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;
}
如果集合是
Set
s

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 -> {});
        });
    });
}

我是一个可以证明的白痴


我使用Lombok为我的POJO生成getter/setter等,它的默认实现
@EqualsAndHashCode
注释生成这两种方法,并考虑每个字段。。包括子集合。

我很惊讶Root的子集合实际上是空的

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

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

另外

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

定义从数据库获取数据的策略。渴望的 策略是持久性提供程序运行时的一项要求 必须急切地获取数据。懒惰策略是对 持久性提供程序运行时,在 首先访问它。允许实现急切地获取 已为其指定惰性策略提示的数据


即使这是默认行为,也应该在
@OneToMany
注释中明确添加值
fetch=FetchType.LAZY
。这可能有助于避免这种递归抓取实际上是否有对数据库的调用。。或者集合是作为代理创建的,没有任何调用吗?
私有集子项
将永远不会为空。您是如何加载这些实例的?@AlanHay实际上它不是
null
,我说错了。所以解决方案是使用没有子集合的equals和hashcode?实际上它不是
null
,我说错了。我还更新了我的原始问题,提供了一些有关查询以及查询执行时间的信息。是的。。现在有道理了。。它已针对集合进行了初始化,因为当您添加到集合时,java会调用equals和hashCode方法(列表不是这种情况)。。你的案子很好。。
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 -> {});
        });
    });
}
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_
            });
        });
    });
}