简体   繁体   English

DTO在递归结构上的投影

[英]DTO projection on a recursive structure

In my data model there is an entity "location" which is recursively. 在我的数据模型中,有一个实体“位置”是递归的。 Furthermore there are relations to other entities. 此外,与其他实体也有关系。

The corresponding JPA (Spring Data JPA) entity looks like: 相应的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
  ...

What is the most performant way to do a read only query? 进行只读查询的最有效方式是什么? I just need the information inside the entity (table location) with the complete recursive structure but no information from the related entities. 我只需要具有完整递归结构的实体(表位置)内部的信息,而无需来自相关实体的信息。

I've read the phrase DTO projection, but nothing about what to do with a recursive structure. 我已经读过DTO投影一词,但与递归结构无关。

Reading a recursive structure is usually done by making use of what SQL calls a recuresive CTE . 读取递归结构通常是通过利用SQL称为递归CTE来完成的 JPA does not support that out of the box, because not all RDBMS support it. JPA不立即支持该功能,因为并非所有RDBMS都支持它。 If you know that your DBMS supports it, you can make use of the following SQL to do this: 如果您知道您的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

With that you get a list of a specific and all parent location ids as well as their respective parents which is flat. 这样一来,您将获得一个特定的父级位置ID列表以及它们各自的父级ID。 You will have to bring structure into this. 您将不得不在其中引入结构。

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())));

If you don't want to make use of plain SQL because of portability concerns or just because you don't want to give up on your abstraction, you can make use of Blaze-Persistence which works on top of JPA and adds support for CTEs. 如果您由于可移植性问题而不想使用纯SQL,或者只是因为不想放弃抽象而使用Blaze-Persistence ,则可以在JPA之上使用Blaze-Persistence ,并增加对CTE的支持。 。 Your query with blaze-persistence would look like this 您的查询具有持久性,看起来像这样

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();

You will also need this special entity class 您还将需要此特殊实体类

@CTE
@Entity
public class LocationCte {
  @Id Integer id;
  Integer parent;
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM