EAGER 只在一个级别上工作,它不打算获取树状结构。
我建议您将一对多获取更改为 LAZY,因为EAGER 获取是一种代码异味,并像这样获取根实体:
SELECT e
FROM MyEntity e
LEFT JOIN FETCH e.children
where e.parent is null
您使用递归来获取所有带有附加子选择的图形。
void fetchAll(MyEntity root) {
for(MyEntity child : root.children) {
fetchAll(child);
}
}
一种更有效的方法是完全放弃子集合并在 FK 关联上使用递归 CTE来获取给定树中所有实体的所有 id。然后使用第二个 JPA 查询,您可以通过它们的 id 获取所有实体,并通过匹配 parents来重建树。
更新实际解决方案
我在 GitHub 上添加了一个测试来为此提供解决方案。考虑以下实体:
@Entity(name = "Node")
public class Node {
@Id
@GeneratedValue
private Long id;
@ManyToOne
@JoinColumn(name = "parent_id")
private Node parent;
//This must be transient as otherwise it will trigger an additional collection fetch
//@OneToMany(mappedBy = "parent", cascade = CascadeType.ALL)
@Transient
private List<Node> children = new ArrayList<>();
public Node() {}
public Node getParent() {
return parent;
}
public List<Node> getChildren() {
return children;
}
public void addChild(Node child) {
children.add(child);
child.parent = this;
}
}
以下转换器可以根据需要重建整个树
Node root = (Node) doInHibernate(session -> {
return session
.createSQLQuery(
"SELECT * " +
"FROM Node " +
"CONNECT BY PRIOR id = parent_id " +
"START WITH parent_id IS NULL ")
.addEntity(Node.class)
.setResultTransformer(new ResultTransformer() {
@Override
public Object transformTuple(Object[] tuple, String[] aliases) {
Node node = (Node) tuple[0];
if(node.parent != null) {
node.parent.addChild(node);
}
return node;
}
@Override
public List transformList(List collection) {
return Collections.singletonList(collection.get(0));
}
})
.uniqueResult();
});