【问题标题】:DTO projection on a recursive structure递归结构上的 DTO 投影
【发布时间】:2018-06-08 06:17:03
【问题描述】:

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

对应的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 投影这个短语,但对如何处理递归结构一无所知。

【问题讨论】:

    标签: java jpa spring-data-jpa dto


    【解决方案1】:

    读取递归结构通常是通过使用 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;
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2018-03-05
      • 1970-01-01
      • 1970-01-01
      • 2015-01-25
      • 2019-08-11
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多