DTO投影在递归结构上

时间:2018-06-08 06:17:04

标签: java jpa spring-data-jpa dto

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

相应的JPA(Spring Data 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
  ...

执行只读查询的最佳方法是什么?我只需要具有完整递归结构的实体(表位置)内的信息,但不需要来自相关实体的信息。

我已经阅读了短语DTO投影,但没有关于如何处理递归结构。

1 个答案:

答案 0 :(得分:0)

读取递归结构通常是通过使用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列表以及它们各自的父级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,或者只是因为不想放弃抽象而使用,可以使用Blaze-Persistence,它在JPA之上并增加了对CTE的支持。具有持久性的查询看起来像这样

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;
}