UUID jako dodatkowe pole w encji a DTO.

0

Chciałem zastosować podejście z UUID jako dodatkowym polem do identyfikacji unikalności encji na podstawie tej prezentacj(o UUID jest gdzieś od 34 minuty):
Zasadniczo chodzi o to, żeby używać pola UUID zamiast ID w metodach equals i hashCode aby uniknąć problemów z porównywaniem obiektów czy dodawaniu ich do kolekcji.
Mam jednak problem z tym jak prawidłowo zaprojektować DTO do takiej encji i metody equals i hashCode w DTO.
Przykładowo mam taką encję:

@Table("users")
@Entity
public class UserEntity {

    @Id
    @GeneratedValue
    private Long id;


    @Column(updatable = false, nullable = false, unique = true)
    private UUID uuid= UUID.randomUUID();

    private String firstName;

    private String lastName;

    private String mail;

    //gettery i settery

    @Override
    public boolean equals(Object that) {
        return this == that || that instanceof UserEntity 
                && Objects.equals(uuid, ((UserEntity ) that).uuid);
    }

    @Override
    public int hashCode() {
        return Objects.hashCode(uuid);
    }
}

Wszystko wydaje się ok, tylko zastanawia mnie jedna rzecz kiedy inicjalizować to pole UUID? Załóżmy, że mamy serwisy restowe, które służą do prostego CRUDa, do transportu danych służy mi DTO, które jest potem mapowane na encję. Przykładowo takie:


public class UserDto {

    private Long id;
    private String firstName;
    private String lastName;
    private String mail;

    //gettery oraz builder

}

Jak takie DTO powinno wyglądać, czy powinno w sobie zawierać UUID? Jeśli tak to czy to oznacza, że jeśli tworzę nowy rekord i przesyłam dane z frontu za pomocą mojego UserDto, to dodaję w nim pole UUID i od razu inicializuje przy instancjowaniu obiektu DTO a nie encyjnym? Wtedy musiałbym usunąc inicjalizację w klasie encyjnej i po prostu dodać setter za pomocą którego ustawię wartość UUID z dto.
Jeśli inicjalizacja pola UUID miałaby nadal pozostać w encji to wtedy podczas tworzenia obiektu UUID w DTO byłby nullem. Nie miałoby to jakichś negatywnych skutków? Chodzi mi głownie o metody hashCode i equals w DTO, ponieważ zastanawiam się jak powinny one wyglądać w klasie DTO. Jak je prawidłowo zaprojektować?

Przy pobieraniu danych już nie miałbym tego problemu z UUID bo generujemy go tylko raz, więc zmapowałbym dane z encji na dto i po kłopocie.

0

Ja bym na twoim miejscu zrobił dwie klasy DTO, jedną do zapisu która nie miała by UUID'a bo ten byłby generowany po stronie serwera bezpośrednio w encji, i drugą do aktualizacji która miałaby ten UUID i dodał settera / ustawiał przez konstruktor / mapper. Ewentualnie nic nie stoi na przeszkodzie aby generować nowy UUID przy aktualizacji i tak będziesz miał ID jako zachowanie ciągłości tożsamości rekordu.

0

@MrMadMatt: generowanie za każdym razem nowego UUID raczej jest słabym pomysłem, założeniem tutaj jest żeby był przez cały czas niezmienny.
Podejście z DTO w którym ustawiam UUID podczas edycji też raczej jest zbędne ponieważ nie będę aktualizował tego pola podczas updatów, mam ustawione w column updatable na false.
Bardziej chodzi mi o te equals i hashCode czy ma to w ogóle sens? Czy nie przeciążać tych metod. W zasadzie DTO powinny służyć tylko do wyświetlania danych, ale w tym przypadku przenoszą dane jakie mają zostać zapisane bądź zedytowane.

Może ktoś inny z bardziej doświadczonych osób jest mi w stanie wyjaśnić jakie jest najlepsze podejście? @Shalom @jarekr000000

0

Nie wiem dokładnie co próbujesz zrobić, ale UUID możesz przecież mieć w ścieżce endpointu i zresztą zwykle tak się robi, tzn jakieś /edit/1234-dupa a nie /edit/ i w payloadzie ID. Ale to jest moim zdaniem szczegół i zawracasz sobie głowę duperelami. Zrób tak żeby było ci wygodnie.

0

A po co ci te UUID? W kontekście bazy danych ja nie widzę różnicy w funkcjonalności między UUID a zwykłym id. UUID przydałoby ci się jakbyś robił porównywanie encji zanim ją spersystujesz i baza wygeneruje dla niej unikalne id.

1

Moja rada: wywal z tego projektu hibernate/jpa albo ewentualnie W OGÓLE nie posługuj się NIGDZIE w aplikacji tymi @Entity DTOsami. Wyciągnij co chcesz z bazy, przemapuj od razu na jakieś obiekty domenowe i voila. Analogicznie w drugą stronę, mapuj na te entity tylko przed samym zapisem do bazy. Nagle wszelkie problemy znikają jak ręką odjął. Bo teraz to wygląda na klasyczne dzielne walczenie z problemami nie znanymi w innych technologiach ;]

0

@MrMadMatt:
W CRUDzie taka sytuacja raczej nie powinna mieć sytuacja. A problem OP by nie istniał w ogóle, gdyby w takiej sytuacji nie operował na encjach JPA w logice biznesowej, a na zwykłych domenowych klasach javowych. Wtedy generowanie UUID jak i operacje na mapach, czy czymkolwiek innym, odbywałoby się poza flow JPA. No ale wracamy do kwestii tego, że to CRUD - tu nie ma logiki więc imo użycie UUID to sztuka dla sztuki.

0

Chyba zostałem źle zrozumiany ponieważ ja nigdzie nie korzystam z Entity jak z obiektów biznesowych. Po pobraniu z dao są one od razu mapowane na obiekty. W moim przypadku od razu na to DTO ponieważ jest to prosta aplikacja typu CRUD.

Mam swoje entity usera, które jest mapowane na DTO i odwrotnie. Przy tworzeniu obiektu mam po prostu DTO, który odbieram w controllerze a następnie waliduje, jeśli walidacja przejdzie pomyślnie to mapuje to na Encje i zapisuje. Żadnej logiki nie robię na encji.
Przy pobieraniu obiektu do edycji albo listy po prostu w serwisie pobieram z dao encje i mapuje na DTO, które przesyłam na front.

Mój endpoint do edycji nie wygląda w stylu edit/ i w payloadzie ID tylko jest to edit/{id}

I tutaj moje pytanie czy te DTO powinny być odpowiedzialne za tworzenie unikalnego UUID i czy jest sens umieszczać to UUID w DTOsach jeśli w zasadzie służy on do transportu danych. I czy takie DTO powinno mieć przeciążane equals i hascode z użyciem UUID tak jak encja?

0

Generalnie zrozumiałbym gdyby ktoś generował sobie jakiegoś uida zamiast idka numerycznego, żeby mieć identyfikator encji zanim zostanie on zapisany do bazy (i ktoś miałby ku temu powód). Generalnie dawno nie pracowałem z hibernatem i nie rozumiem czemu ID nie mógłby byc w hashcodzie?

I tutaj moje pytanie czy te DTO powinny być odpowiedzialne za tworzenie unikalnego UUID i czy jest sens umieszczać to UUID w DTOsach jeśli w zasadzie służy on do transportu danych. I czy takie DTO powinno mieć przeciążane equals i hascode z użyciem UUID tak jak encja?

Ale po co? Generalnie hashcode i equals mozesz miec na podstawie ID i typu, jeżeli będziesz chciał porównywać DTOsy (których używasz zamiast obiektów domenowych, bo masz prosta apke) to porównujesz je w taki sam sposób, w większosci przypadków to wlasnie identyfikator obiektu odroznia go od innych obiektów np 2 osoby mogą miec tak samo na imie i nazwisko, ale to są rózne osoby, z pominięciem sytuacji w których obiekt posiada jakiś klucz naturalny, np numer VIN dla samochodu lub kilka własciwosci obiektów będzie dany obiekt identyfikować. Co do pytania czy equals i hashcode taki sam jak w encji - generalnie tak, w końcu te 2 obiekty (DTO i encja) reprezentują ten sam byt, tylko uzywasz ich w różnych warstwach aplikacji żeby oddzielić od siebie abstrakcje

0
hcubyc napisał(a):

Generalnie zrozumiałbym gdyby ktoś generował sobie jakiegoś uida zamiast idka numerycznego, żeby mieć identyfikator encji zanim zostanie on zapisany do bazy (i ktoś miałby ku temu powód). Generalnie dawno nie pracowałem z hibernatem i nie rozumiem czemu ID nie mógłby byc w hashcodzie?

Chodzi o np. nowo utworzone encje, które nie mają jeszcze id bo nie nadajemy sami id tylko mamy ustawione @GeneratedValue w encji i baza za nas to robi. Przykładowo mamy jakąś encję, która ma w sobie relacje i przechowuje je w secie, to jak wtedy dodamy kilka nowo utworzonych encji bez id, które mają equals i hashcode na bazie id?

0

No to możesz generować UUID dla takich encji, który będzie jednocześnie ich ID. Przy czym nie musi być to zwykły UUID, jest wiele libek, które pozwalają generować unikalne (do pewnego stopnia) IDki a'la youtube.

Przykładowo mamy jakąś encję, która ma w sobie relacje i przechowuje je w secie, to jak wtedy dodamy kilka nowo utworzonych encji bez id, które mają equals i hashcode na bazie id?

Porównywać na podstawie domyslnego hashcoda i equalsa jeżeli nie ma ID? Lub jeżeli encja ma taką możliwość to na podstawie zbioru własciwosci obiektu (np. e-mail jest unikalny, wiec mozesz porownac uzytkownikow na podstawie e-maila).
Generalnie pomysł z UUIDem nie jest zły, ale opędza tylko jeden przypadek, a i tak musiałbyś kopiować (albo miec jakas super klase) te metode dla każdej klasy, więc jak dla mnie to srednie rozwiazanie problemu. Inna sprawa to pytanie czy przypadek pozwala żeby najpierw zapisac obiekty do bazy, a pozniej ustawic im relacje?

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