我有一个树形结构,其中每个Node
都有一个父项和一个Set<Node> children
。每个节点都有String title
,我想在我选择Set<String> titles
的位置进行查询,作为此节点和所有父节点的标题。我该如何撰写此查询?
单个标题的查询是这样的,但就像我说的那样,我希望它扩展到父母的整个分支。
SELECT node.title FROM Node node WHERE node.id = :id
干杯
的Nik
答案 0 :(得分:14)
您不能使用HQL进行递归查询。 See this。如上所述,它甚至不是标准的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
}
我肯定会选择第二种选择。
答案 1 :(得分:9)
虽然无法编写您要求的递归查询,但可以使用HQL来获取层次结构;这样做至少可以让你在内存中遍历树而不需要为每个级别访问数据库。
select n from Node n
left join fetch n.Children
答案 2 :(得分:0)
我知道这个问题很旧,但是由于它与另一个问题相关联,因此我想对此进行更新,因为Blaze-Persistence支持在JPA模型之上使用递归CTE。< / p>
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.com/documentation/core/manual/en_US/index.html#recursive-ctes