【发布时间】:2017-06-26 06:57:16
【问题描述】:
当前堆栈:
- Spring Boot 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 仅从相应的表中选择,结果对象的 children Set 是 一个 Hibernate 代理 - 应该是这样。 null
当我调用 getChildren() 时,Hibernate 正确地初始化了集合,但也(毫无根据地)获取了每个 Child 对象的 grandchildren 集合。
有人能解释一下为什么会发生这种递归提取吗?有没有办法禁用它?
我做了更多的挖掘,这就是我想出的:它似乎与 Hibernate 映射 @OneToMany 的方式有关,具体取决于目标集合是 List 还是 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 注释的默认实现会生成这两种方法,同时考虑到每个字段......包括子集合。
【问题讨论】:
-
即使这是默认行为,您也应该在
@OneToMany注释中明确添加值fetch = FetchType.LAZY。这可能有助于避免这种递归获取 -
实际上是否有对数据库的调用.. 还是将集合创建为没有任何调用的代理?
-
private Set<Child> children永远不会为空。你是如何加载这些实例的? -
@AlanHay 确实不是
null,我说错了。 -
所以解决方案是拥有一个没有子集合的等号和哈希码?
标签: java spring hibernate jpa fetching-strategy