选项 1
换个角度想,在Child 上搜索并获得他们的Parent 怎么样。
将新的构造函数添加到Parent 类中
public Parent(Integer id, String name, Child child) {
this.id = id;
this.name = name;
this.childs = new HashSet<>();
this.childs.add(child);
}
public Parent() {
}
使用以下脚本更改 JPA 查询。
@Query("select new Parent(c.parent.id, c.parent.name, c) from Child c where c.name = 'child1'")
List<Parent> getParent();
Hibernate 为两个 Child 记录生成 3 个 SQL(所有 Child 计数中的多个查询)
Hibernate: select child0_.parent_id as col_0_0_, parent1_.name as col_1_0_, child0_.id as col_2_0_ from childs child0_ cross join parents parent1_ where child0_.parent_id=parent1_.id and child0_.name='child1'
Hibernate: select child0_.id as id1_0_0_, child0_.name as name2_0_0_, child0_.parent_id as parent_i3_0_0_ from childs child0_ where child0_.id=?
Hibernate: select child0_.id as id1_0_0_, child0_.name as name2_0_0_, child0_.parent_id as parent_i3_0_0_ from childs child0_ where child0_.id=?
如果只执行一条 SQL,可以升级下面的Parent 类和 Jpa 查询。
父构造函数:
public Parent(Integer id, String name, Integer childId, String childName) {
this.id = id;
this.name = name;
this.childs = new HashSet<>();
Child child = new Child();
child.setId(childId);
child.setName(childName);
this.childs.add(child);
}
在 jpa 查询中添加了新参数
@Query("select new Parent(c.parent.id, c.parent.name, c.id, c.name) from Child c where c.name = 'child1'")
List<Parent> getParent();
Hibernate 生成单个查询。
Hibernate: select child0_.parent_id as col_0_0_, parent1_.name as col_1_0_, child0_.id as col_2_0_, child0_.name as col_3_0_ from childs child0_ cross join parents parent1_ where child0_.parent_id=parent1_.id and child0_.name='child1'
测试
@SpringBootTest
class DemoApplicationTests {
@Autowired
private ParentRepository parentRepository;
@Test
void contextLoads() {
Parent parent = new Parent();
parent.setName("p1");
parent.setChilds(new HashSet<>());
parent.getChilds().add(new Child("child1", parent));
parent.getChilds().add(new Child("child2", parent));
parentRepository.save(parent);
parent = new Parent();
parent.setName("p2");
parent.setChilds(new HashSet<>());
parent.getChilds().add(new Child("child1", parent));
parentRepository.save(parent);
List<Parent> parents = parentRepository.getParent();
Assertions.assertEquals(2, parents.size());
for (Parent parent1 : parents) {
System.out.println(parent1);
}
}
}
输出
Parent(id=1, name=p1, childs=[Child(id=3, name=child1)])
Parent(id=4, name=p2, childs=[Child(id=5, name=child1)])
选项 2
获取所有子节点并在 java 端管理 Parent。
public interface ChildRepository extends JpaRepository<Child, Integer> {
@Query("select c from Child c left join fetch c.parent where c.name = 'child1'")
List<Child> search();
}
Hibernate 生成单个 SQL。
Hibernate: select child0_.id as id1_0_0_, parent1_.id as id1_1_1_, child0_.name as name2_0_0_, child0_.parent_id as parent_i3_0_0_, parent1_.name as name2_1_1_ from childs child0_ left outer join parents parent1_ on child0_.parent_id=parent1_.id where child0_.name='child1'
测试
@SpringBootTest
class DemoApplicationTests2 {
@Autowired
private ParentRepository parentRepository;
@Autowired
private ChildRepository childRepository;
@Test
void contextLoads() {
Parent parent = new Parent();
parent.setName("p1");
parent.setChilds(new HashSet<>());
parent.getChilds().add(new Child("child1", parent));
parent.getChilds().add(new Child("child2", parent));
parentRepository.save(parent);
parent = new Parent();
parent.setName("p2");
parent.setChilds(new HashSet<>());
parent.getChilds().add(new Child("child1", parent));
parentRepository.save(parent);
// search children
List<Child> children = childRepository.search();
// create parent hash map
Map<Integer, Parent> parentMap = children.stream()
.map(Child::getParent)
.distinct()
.peek(p -> {
p.setChilds(new HashSet<>());
})
.collect(Collectors.toMap(Parent::getId, p -> p));
// add children manually
for (Child child : children) {
parentMap.get(child.getParent().getId()).getChilds().add(child);
}
// get parents
Collection<Parent> parents = parentMap.values();
Assertions.assertEquals(2, parents.size());
for (Parent parent1 : parents) {
System.out.println(parent1);
}
}
}
输出
Parent(id=1, name=p1, childs=[Child(id=3, name=child1)])
Parent(id=4, name=p2, childs=[Child(id=5, name=child1)])
额外
获取所有Parent 并填充匹配的Child 记录。我们需要对预定义的 Parent 构造函数进行一些更改,并将 where 的新 JPA 查询替换为 and 关键字。
父构造函数:
有一个额外的childId 控件来避免插入空子记录。
public Parent(Integer id, String name, Integer childId, String childName) {
this.id = id;
this.name = name;
this.childs = new HashSet<>();
if (childId != null) {
Child child = new Child();
child.setId(childId);
child.setName(childName);
this.childs.add(child);
}
}
ParentRepository:没有where键,因为它限制了我们的结果,我们只需要and而不是它。
@Query("select new Parent(p.id, p.name, c.id, c.name) from Parent p left outer join Child c on c.parent = p and c.name = 'child2'")
List<Parent> getParent();
休眠查询:
刚刚执行了一个查询。
Hibernate: select parent0_.id as col_0_0_, parent0_.name as col_1_0_, child1_.id as col_2_0_, child1_.name as col_3_0_ from parents parent0_ left outer join childs child1_ on (child1_.parent_id=parent0_.id and child1_.name='child2')
测试
List<Parent> parents = parentRepository.getParent();
Assertions.assertEquals(2, parents.size());
for (Parent parent1 : parents) {
System.out.println(parent1);
}
输出:我们正在搜索child2,一个记录有这个,另一个是空的。
Parent(id=1, name=p1, childs=[Child(id=2, name=child2)])
Parent(id=4, name=p2, childs=[])