Java 递归结构上的DTO投影

Java 递归结构上的DTO投影,java,jpa,spring-data-jpa,dto,Java,Jpa,Spring Data Jpa,Dto,在我的数据模型中,有一个递归的实体“位置”。此外,还有与其他实体的关系 相应的JPA(Spring数据JPA)实体如下所示: @Entity @Table(name = "location") class Location{ @OneToMany(mappedBy = "parent", orphanRemoval = true) @OrderBy("name ASC") Set<Location> children = null @ManyToOne(fetch

在我的数据模型中,有一个递归的实体“位置”。此外,还有与其他实体的关系

相应的JPA(Spring数据JPA)实体如下所示:

@Entity
@Table(name = "location")
class Location{

  @OneToMany(mappedBy = "parent", orphanRemoval = true)
  @OrderBy("name ASC")
  Set<Location> children = null

  @ManyToOne(fetch = FetchType.EAGER)
  @JoinColumn(name = "parent_id")
  Location parent = null

  @Column(name = "name")
  String name = null

  @OneToMany(mappedBy = "location", fetch = FetchType.EAGER)
  Stops stops = null
  ...
@实体
@表(name=“location”)
类位置{
@OneToMany(mappedBy=“parent”,orphanRemoving=true)
@订购人(“名称ASC”)
设置children=null
@manytone(fetch=FetchType.EAGER)
@JoinColumn(name=“parent\u id”)
位置父项=null
@列(name=“name”)
字符串名称=null
@OneToMany(mappedBy=“location”,fetch=FetchType.EAGER)
停止=空
...
执行只读查询最有效的方法是什么?我只需要实体(表位置)内部的信息,具有完整的递归结构,但不需要来自相关实体的信息


我读过短语DTO projection,但没有关于如何处理递归结构的内容。

读取递归结构通常是通过使用SQL所称的递归CTE来完成的。JPA不支持开箱即用,因为并非所有RDBMS都支持它。如果您知道您的DBMS支持它,您可以使用以下方法要执行此操作,请执行以下操作:

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的列表,以及它们各自的父位置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())));
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;
}