MapStruct nieskończona pętla (cykle)

0

Cześć
Pisałem ostatnio pewną aplikację (cos takiego robiłem pierwszy raz, więc zawiera baardzo bardzo duzo złych praktyk ).

No więc składa się ona z backendu w Springu, który mi tam wystawia jakies "RESTowe" API (z restem ma niewiele wspólnego, no ale controllery zwracaja mi JSONy). Druga część aplikacji to frontend w javaFX. Pomyślałem sobie że fajnie byłoby aby dane między aplikacja kliencka, a serwerem były przesyłane za posrednictwem obiektów na wzór DTO. Wydzieliłem więc jeszcze jeden moduł aplikacji, z którego korzysta zarówno backend jak i frontend.

Tylko teraz w czym problem.... Widziałem tutaj na forum dyskusję, że @Entity z JPA/Hibernate , to tak naprawdę prawie DTO. Ja jednak chciałbym je zmapować na swoje DTO (nawet jesli to będzie na chwilę obecną praktycznie 1:1) i używać tych samych szablonów w obu aplikacjach (fx, spring). Dołączyłem do mapowania framework mapstruct.
Problem pojawia się jesli chcę mapować klasę, która jest w relacji wiele do jednego z inną klasą. Mapstruct wtedy popada w nieskończoną rekurencję, dostaję stackoverflow i po marzeniach.

Poniżej link do modelu bazy danych
https://www.vertabelo.com/blog/technical-articles/a-database-model-for-a-movie-theater-reservation-system

Problem dotyczy mapowania klasy Auditorium, która ma składową Seat. Seat zaś ma składową Auditorium.
Jak to obejść? Czy powinienem zmodyfikować swoje DTOski, tak aby nie miały cykli?

0
@Data
@JsonIgnoreProperties(ignoreUnknown = true)
public class AuditoriumDTO {

    private int id;
    private String name;
    private Integer rows;
    private Integer cols;
    private List<SeatDTO> seats;
}

@Data
@JsonIgnoreProperties(ignoreUnknown = true)
public class SeatDTO {
    private int id;
    private int row;
    private int number;
    private boolean active;
    private AuditoriumDTO auditorium;

}

1

Po co w SeatDTO referencja do AuditoriumDTO?

1

Jeśli dodałeś referencję Auditorium do obiektu Seat, tylko po to, żeby wygodnie się do niego dostać, to musisz inaczej to rozwiązać. DTO to jednak coś innego niż JPA entities - w klasach Entity masz pola w obu, żeby pokazać jaka jest relacja między obiektami.

Głównym celem DTO jest dostarczanie klientom danych do widoków. Jeśli np. masz w apce widok, który wyświetla szczegóły Auditorium, wtedy sobie wydzielasz np. package rest.auditorium, a w nim DTOs tylko dla rest endpointa, który udostępnia dane dla tego widoku, np:

@Data
@JsonIgnoreProperties(ignoreUnknown = true)
public class AuditoriumDTO {
 
    private int id;
    private String name;
    private Integer rows;
    private Integer cols;
    private List<SeatDTO> seats;
}
 
@Data
@JsonIgnoreProperties(ignoreUnknown = true)
public class SeatDTO {
    private int id;
    private int row;
    private int number;
}

i kontroler, który zwraca dane dla tego widoku.

Jeśli masz też widok, w którym oglądasz szczegóły siedzenia, to sobie wydzielasz package rest.seat, w którym też definiujesz tylko to co potrzebne dla tego widoku, np.

@Data
@JsonIgnoreProperties(ignoreUnknown = true)
public class AuditoriumDTO {
    private int id;
    private String name;
}
 
@Data
@JsonIgnoreProperties(ignoreUnknown = true)
public class SeatDTO {
    private int id;
    private int row;
    private int number;
    private boolean active;
    private AuditoriumDTO auditorium;
}

I do tego jeszcze kontroler

Może się wydawać, że to trochę duplikowanie kodu ale każdy widok wygląda inaczej, może się zmieniać niezależnie od drugiego, więc korzystanie ze wspólnych DTO z reguły wiąże się z wyższym kosztem utrzymania takiego kodu. Podsumowując - masz 1 zestaw klas @Entity, ale DTOsy lepiej mieć osobne dla każdego z widoków. Jeśli chcesz współdzielić embedded DTO pomiędzy różnymi widokami - rób to ostrożnie.

1 użytkowników online, w tym zalogowanych: 0, gości: 1