读取递归结构通常是通过使用 SQL 所称的递归 CTE 来完成的。 JPA 不支持开箱即用,因为并非所有 RDBMS 都支持它。如果您知道您的 DBMS 支持它,您可以使用以下 SQL 来执行此操作:
WITH RECURSIVE nodes(id, parent_id) AS (
SELECT id, parent_id FROM location l where id = ?
UNION ALL
SELECT l.id, l.parent_id FROM nodes n JOIN location l ON n.parent_id = l.id
)
SELECT id, parent_id FROM nodes
这样,您将获得一个特定和所有父位置 ID 的列表,以及它们各自的父位置,这是平坦的。您必须将结构纳入其中。
List<Object[]> result = //get the result of the query
Map<Integer, LocationDto> locationMap = new HashMap<>();
result.forEach(r -> locationMap.put(result.get(0), new LocationDto(result[0], result[1])));
locationMap.values().forEach(l -> l.setParent(locaitonMap.get(l.getParentId())));
如果您因为可移植性问题或不想放弃抽象而不想使用纯 SQL,则可以使用在 JPA 之上工作并添加的 Blaze-Persistence支持 CTE。您的 blaze-persistence 查询看起来像这样
List<LocationCte> result = criteriaBuilderFactory.create(entityManager, LocationCte.class)
.withRecursive(LocationCte.class)
.from(Location.class, "l")
.bind("id").select("l.id")
.bind("parent").select("l.parent.id")
.where("id").eq(initialId)
.unionAll()
.from(Location.class, "l")
.innerJoinOn(LocationCte.class, "cte")
.on("cte.parent").eqExpression("l.id)
.end()
.bind("id").select("l.id")
.bind("parent").select("l.parent.id")
.end()
.from(LocationCte.class)
.getResultList();
你还需要这个特殊的实体类
@CTE
@Entity
public class LocationCte {
@Id Integer id;
Integer parent;
}