我有一个树形结构,其中每个 Node
有一个父母和一个 Set<Node> children
. 每个节点有一个 String title
,我想做一个查询,我选择了 Set<String> titles
,是这个节点和所有父节点的标题。如何写这个查询?
对单个标题的查询是这样的,但就像我说的,我希望它能扩展到整个父节点的分支。
SELECT node.title FROM Node node WHERE node.id = :id
干杯
尼
你不能用HQL做递归查询。请看这个. 而且正如那里所说,它甚至不是标准的SQL。你有两个选择。
进行多次查询。比如说。
// obtain the first node using your query
while (currentNode.parent != null) {
Query q = //create the query
q.setParameter("id", currentNode.getParentId());
Node currentNode = (Node) q.getSingleResult();
nodes.add(currentNode); // this is the Set
}
我肯定会选择第二种方案。
虽然不可能写出你所要求的递归查询,但可以用HQL热切地获取层次结构;这样做至少可以让你在内存中走树,而不用为每一层打数据库。
select n from Node n
left join fetch n.Children
我知道这个问题是老问题了,但由于在另一个问题中链接过,我想给大家更新一下,因为 燃烧-持久性 提供了对JPA模型之上的递归CTE的支持。
Blaze-Persistence是一个JPA之上的查询构建器,它支持JPA模型之上的许多高级DBMS功能。要对CTE或递归CTE进行建模,也就是我们这里所需要的,首先需要引入一个CTE实体,对CTE的结果类型进行建模。
@CTE
@Entity
public class NodeCTE {
@Id Integer id;
}
你的例子中的一个查询可以看起来像下面这样
List<String> titles = criteriaBuilderFactory.create(entityManager, String.class)
.withRecursive(NodeCTE.class)
.from(Node.class, "n1")
.bind("id").select("n1.id")
.where("n1.id").eq(nodeId)
.unionAll()
.from(Node.class, "n2")
.innerJoinOn(NodeCTE.class, "cte")
.on("cte.id").eq("n2.parent.id")
.end()
.bind("id").select("n2.id")
.end()
.from(Node.class, "n")
.select("n.title")
.where("n.id").in()
.from(NodeCTE.class, "c")
.select("c.id")
.end()
.getResultList();
这在SQL中的表现是这样的。
WITH RECURSIVE NodeCTE(id) AS (
SELECT n1.id
FROM Node n1
WHERE n1.parent_id = :id
UNION ALL
SELECT n2.id
FROM Node n2
INNER JOIN NodeCTE cte ON n2.parent_id = cte.id
)
SELECT n.title
FROM Node n
WHERE n.id IN (
SELECT c.id
FROM NodeCTE c
)
你可以在文档中找到更多关于递归CTE的信息。https:/persistence.blazebit.comdocumentationcoremanualen_USindex.html#recursive-ctes。