Проекция DTO на рекурсивную структуру - PullRequest
0 голосов
/ 08 июня 2018

В моей модели данных есть сущность "местоположение", которая является рекурсивной.Кроме того, существуют отношения с другими объектами.

Соответствующий объект 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 projection, но ничего о том, что делать с рекурсивной структурой.

1 Ответ

0 голосов
/ 19 июля 2018

Чтение рекурсивной структуры обычно выполняется с использованием того, что SQL называет рекурсивным CTE .JPA не поддерживает это из коробки, потому что не все RDBMS поддерживают это.Если вы знаете, что ваша СУБД поддерживает это, вы можете использовать следующий 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

С этим вы получите список определенных и всех родительских идентификаторов местоположения, а также их соответствующих родителейкоторый плоский.Вам придется внести в это структуру.

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.Ваш запрос с 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;
}
...