Uniwersalne DTO?

0

Witam,

Borykam się z takim małym problemem, otóż, korzystam ze Spring boot'a + Hibernate, przekazuję do modelu obiekt Usera, który ma powiązania z innymi encjami.
Użytkownik edytuje pola Usera (np zmienia dane osobowe etc) i w metodzie POST łapię ten obiekt, jednak encje, które były w relacji z tym obiektem, mają wartości null.

Znajomy zaproponował mi stworzenie klasy DTO, która będzie posiadała wszystkie pola głównej encji oraz tych encji, które zawiera - mówiąc prościej stworzyć klasę, która będzie miała +30 pól/getterów/setterów.
Od razu mi się to nie spodobało i szukam sposobu, jak stworzyć uniwersalną klasę DTO, która nie będzie tak obszerną klasą i dodatkowo będzie mogła być używana wielokrotnie, nie tylko dla encji User.

Czy są jakieś gotowe rozwiązania (biblioteki, mappery) ?
A może istnieje rozwiązanie, jak przekazać taki obiekt do modelu, a po "zwróceniu" tego obiektu wszystkie relacje zostaną zachowane?

Próbowałem stworzyć klasę DTO, która zawiera Map<String, String> properties, a w encji, sobie tworzyłem taką mapę, miałem z tym jednak problemy i porzuciłem pomysł - czy może wcale nie był taki głupi?

0

lepiej tak nie robić jak kaliega polecił
jedna encja - jeden DTOs
a jak chcesz sobie ułatwić trochę życie z getterami/setterami, to może skorzystaj z Lomboka

0

To nie ma sensu, zrób jedną DTO na jeden dany endpoint czy cokolwiek co tam masz. A poza tym sądze że settery musza być zniszczone

0

Tylko problem jest taki, że mam w encji Foo obiekt encji Bar. I w metodzie post po wypełnieniu formularza przez uzytkownika, w encji Foo obiekt Bar jest nullem.

A do tego modelu przekazuję obiekt (w metodzie GET) i on posiada ten obiekt, a z powrotem dostaje z nullami

0

To znaczy że masz coś nie tak zmapowany DTOs. Jutro pokaże kod jak prawilnie to zrobić żeby się to dobrze mapowało

0
@Entity
class User {
    private String login;
    private String password;

    @OneToOne
    @JoinColumn("id")
    private UserDetails userDetails;

    // getters and setters
}

@Entity
class UserDetails {
    private String name;
    private String surname;
    private String email;
    private String address;
    private String country;
    private String postCode;
    private String city;
    private String pesel;

    @OneToOne
    @JoinColumn("id")
    Employee employee;

    @OneToMany
    @JoinColumn("id")
     List<WorkTime> worktime;

    // getters and setters
}

@Entity
class Worktime {
    private Date dateIn;
    private Date dateOut;
    private Integer hours;

    @ManyToOne
    @JoinColumn("id"
    Employee employee;

    // getters and setters
}

Wrzucam dla przykładu taki pseudokod, który ma tylko zobrazować +/- co mam na myśli. Mam encję User, w której trzymam tylko najważniejsze informacje na temat usera, w innych encjach, trzymam bardziej szczegółowe informacje.

@GetMapping("/edit_user/{id}")
public String editUser(@PathVariable Long id, Model model) {
    model.addAttribute("user", userService.getById(id));
    return "editUser";
}

@PostMapping("/edit_User")
public String editUser(@Valid @ModelAttribute("user") User user,
                BindingResult result, ModelMap model)  {
    userService.saveOrUpdate(user); // Tutaj obiekt user, jest całkiem inny,
                                                          //  niż ten który przekazałem do modelu w GET
    return "redirect:/home";
}
0

@wyebani: nie powinno się wrzucać encji JPA do warstwy API... ona nie powinna nigdy opuszczać warstwy logiki biznesowej!

0
scibi92 napisał(a):

@wyebani: nie powinno się wrzucać encji JPA do warstwy API... ona nie powinna nigdy opuszczać warstwy logiki biznesowej!

Więc jak rozwiązać ten problem, jak poprawnie zaimplementować edycję użytkownika?

2

To co robisz nie ma nic wspólnego z DTO. Jak sama nazwa wskazuje DTO to Data Transfer Object a więc obiekty służące do wysyłania jakichś tam danych pomiędzy frontem a backendem lub między innymi usługami. To co napisał @scibi92 ma sens ponieważ w Twoim przypadku Twój frontend jest powiązany z backendem i dlatego masz takie problemy. Dodatkowo zauważ że wystarczy że będziesz chciał zmienić jakiś formularz czy cokolwiek na froncie i automatycznie twoja encja bazodanowa też będzie musiała być zmieniona a więc modyfikując frontend automatycznie musisz modyfikować bazę danych. Bezsensu.

Rozwiązaniem jest odseparowanie jednego od drugiego. Frontend sobie a logika sobie. Frontent niech ma formularze, tabelki i inne takie zbudowane w taki sposób jak tego potrzebujesz i na tę potrzeby dodawaj sobie nowe DTO... Np. masz rejestrację użytkownika więc potrzebujesz powiedzmy hasło i powtórzone hasło aby to zwalidować. Podobnie w powtarzaniem maila... Na backendzie w bazie danych tego nie potrzebujesz więc tworzysz sobie np. RegisterAccountDto który ma wszystkie potrzebne dla Ciebie pola i taki też obiekt przyjmujesz w kontrolerze. Kontroler robi co ma tam zrobić a następnie na podstawie dto robiony jest obiekt domenowy np. Account, User czy co tam potrzebujesz. Dopiero taki User trafia do bazy (inna sprawa że logika biznesowa też nie do końca powinna być zależna od bazy danych ale już to bym tutaj pominął).
Natomiast w drugą stronę możesz robić bardzo podobnie. Jeśli chcesz wyjąć coś z bazy danych i wyświetlić na froncie to też nie potrzebujesz przepychać pół bazy tylko po to aby wyświelić coś na stronie (np. Witaj jkowalski). Możesz skorzystać z projekcji albo innych mądrych rad poleconych w tym temacie Konwersja Entity na wiele DTO

W skrócie, porzuć pomysły z jakimiś generykami dto, utwórz prawdziwe dto, odseparuj to od logiki i dodaj jakiś interfejs komunikacyjny między nimi (jakiś serwis, fasade lub cokolwiek podobnego) i będzie śmigać.

0

@eL: Dzięki za odpowiedź. Zdaje sobie sprawę, że te rozwiązanie jest nie dobre. Jest to w sumie mój pierwszy projekt WEB i w dodatku oparty o frameworki (Spring Boot+Hibernate).
Przemyślę sprawę czy da się to co do tej pory napisałem, jakoś rozwiązać w miarę "bezboleśnie". Tak jak pisałem, to mój pierwszy projekt więc jeśli chodzi o architekturę to zdaje sobie sprawę, że robi się to inaczej.

Jeszcze raz dzięki.

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