UUID jako dodatkowe pole w encji a DTO.

UUID jako dodatkowe pole w encji a DTO.
O1
  • Rejestracja:ponad 14 lat
  • Ostatnio:dzień
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ę:

Kopiuj
@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:

Kopiuj

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.

edytowany 10x, ostatnio: olek1
UglyMan
Fajnie by było, jakbyś streścił prezentację. Nikt raczej nie będzie oglądał 50-minutowej prezentacji, żeby ci pomóc.
O1
@Tomek Pycia: Od 34 minuty jest ten fragment. 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.
MrMadMatt
  • Rejestracja:ponad 9 lat
  • Ostatnio:13 dni
  • Postów:373
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.

O1
  • Rejestracja:ponad 14 lat
  • Ostatnio:dzień
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

Shalom
  • Rejestracja:około 21 lat
  • Ostatnio:prawie 3 lata
  • Lokalizacja:Space: the final frontier
  • Postów:26433
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.


"Nie brookliński most, ale przemienić w jasny, nowy dzień najsmutniejszą noc - to jest dopiero coś!"
Zobacz pozostały 1 komentarz
Shalom
To ja chyba nie rozumiem po co.
YA
@Shalom: może chodzi o to, że dla nowych obiektów, niektóre pola nie zostaną uzupełnione przed commitem w bazie, jak wówczas sensownie implementować hashcode/equals? W tym przypadku pewnie chodzi o brak wartości ID. Zgaduję ;P
PU
Dokładnie to jest napisane w podesłanym linku :)
Shalom
@pustypawel: o_O ja nie jestem upośledzony, ja rozumiem po co jest equals i hashcode, po prostu nie bardzo rozumiem czemu ktoś chce jeszcze jakieś kolejne ID tam dodawać.
CountZero
  • Rejestracja:ponad 7 lat
  • Ostatnio:10 miesięcy
  • Postów:262
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.

MrMadMatt
Obejrzyj prezentację z pierwszego posta: warto.
CountZero
No, w prezentacji - "pomaga w poradzeniu sobie z porównywaniem encji w stanie odłączonym". W jakiej sytuacji w prostym CRUDZIE to jest w ogóle potrzebne?
MrMadMatt
Z tego co rozumiem z wideo: w sytuacji kiedy encja jest kluczem w mapie i w międzyczasie ją wstawisz do bazy i nadasz ID. Przy współbieżności i cachowaniu encji, tak mi się wydaje. Raz albo dwa widziałem sytuacje gdzie encja gubiła "tożsamość" w kodzie i gdzie pomógł mi UUID.
Shalom
  • Rejestracja:około 21 lat
  • Ostatnio:prawie 3 lata
  • Lokalizacja:Space: the final frontier
  • Postów:26433
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 ;]


"Nie brookliński most, ale przemienić w jasny, nowy dzień najsmutniejszą noc - to jest dopiero coś!"
CountZero
  • Rejestracja:ponad 7 lat
  • Ostatnio:10 miesięcy
  • Postów:262
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.

O1
  • Rejestracja:ponad 14 lat
  • Ostatnio:dzień
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?

hcubyc
  • Rejestracja:ponad 12 lat
  • Ostatnio:ponad 2 lata
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


Limitations are limitless > ##### Ola Nordmann napisał(a)
> Moim językiem ojczystym jest C++ i proszę uszanować to, że piszę po polsku.
edytowany 2x, ostatnio: hcubyc
O1
  • Rejestracja:ponad 14 lat
  • Ostatnio:dzień
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?

hcubyc
  • Rejestracja:ponad 12 lat
  • Ostatnio:ponad 2 lata
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?


Limitations are limitless > ##### Ola Nordmann napisał(a)
> Moim językiem ojczystym jest C++ i proszę uszanować to, że piszę po polsku.
edytowany 2x, ostatnio: hcubyc
Kliknij, aby dodać treść...

Pomoc 1.18.8

Typografia

Edytor obsługuje składnie Markdown, w której pojedynczy akcent *kursywa* oraz _kursywa_ to pochylenie. Z kolei podwójny akcent **pogrubienie** oraz __pogrubienie__ to pogrubienie. Dodanie znaczników ~~strike~~ to przekreślenie.

Możesz dodać formatowanie komendami , , oraz .

Ponieważ dekoracja podkreślenia jest przeznaczona na linki, markdown nie zawiera specjalnej składni dla podkreślenia. Dlatego by dodać podkreślenie, użyj <u>underline</u>.

Komendy formatujące reagują na skróty klawiszowe: Ctrl+B, Ctrl+I, Ctrl+U oraz Ctrl+S.

Linki

By dodać link w edytorze użyj komendy lub użyj składni [title](link). URL umieszczony w linku lub nawet URL umieszczony bezpośrednio w tekście będzie aktywny i klikalny.

Jeżeli chcesz, możesz samodzielnie dodać link: <a href="link">title</a>.

Wewnętrzne odnośniki

Możesz umieścić odnośnik do wewnętrznej podstrony, używając następującej składni: [[Delphi/Kompendium]] lub [[Delphi/Kompendium|kliknij, aby przejść do kompendium]]. Odnośniki mogą prowadzić do Forum 4programmers.net lub np. do Kompendium.

Wspomnienia użytkowników

By wspomnieć użytkownika forum, wpisz w formularzu znak @. Zobaczysz okienko samouzupełniające nazwy użytkowników. Samouzupełnienie dobierze odpowiedni format wspomnienia, zależnie od tego czy w nazwie użytkownika znajduje się spacja.

Znaczniki HTML

Dozwolone jest używanie niektórych znaczników HTML: <a>, <b>, <i>, <kbd>, <del>, <strong>, <dfn>, <pre>, <blockquote>, <hr/>, <sub>, <sup> oraz <img/>.

Skróty klawiszowe

Dodaj kombinację klawiszy komendą notacji klawiszy lub skrótem klawiszowym Alt+K.

Reprezentuj kombinacje klawiszowe używając taga <kbd>. Oddziel od siebie klawisze znakiem plus, np <kbd>Alt+Tab</kbd>.

Indeks górny oraz dolny

Przykład: wpisując H<sub>2</sub>O i m<sup>2</sup> otrzymasz: H2O i m2.

Składnia Tex

By precyzyjnie wyrazić działanie matematyczne, użyj składni Tex.

<tex>arcctg(x) = argtan(\frac{1}{x}) = arcsin(\frac{1}{\sqrt{1+x^2}})</tex>

Kod źródłowy

Krótkie fragmenty kodu

Wszelkie jednolinijkowe instrukcje języka programowania powinny być zawarte pomiędzy obróconymi apostrofami: `kod instrukcji` lub ``console.log(`string`);``.

Kod wielolinijkowy

Dodaj fragment kodu komendą . Fragmenty kodu zajmujące całą lub więcej linijek powinny być umieszczone w wielolinijkowym fragmencie kodu. Znaczniki ``` lub ~~~ umożliwiają kolorowanie różnych języków programowania. Możemy nadać nazwę języka programowania używając auto-uzupełnienia, kod został pokolorowany używając konkretnych ustawień kolorowania składni:

```javascript
document.write('Hello World');
```

Możesz zaznaczyć również już wklejony kod w edytorze, i użyć komendy  by zamienić go w kod. Użyj kombinacji Ctrl+`, by dodać fragment kodu bez oznaczników języka.

Tabelki

Dodaj przykładową tabelkę używając komendy . Przykładowa tabelka składa się z dwóch kolumn, nagłówka i jednego wiersza.

Wygeneruj tabelkę na podstawie szablonu. Oddziel komórki separatorem ; lub |, a następnie zaznacz szablonu.

nazwisko;dziedzina;odkrycie
Pitagoras;mathematics;Pythagorean Theorem
Albert Einstein;physics;General Relativity
Marie Curie, Pierre Curie;chemistry;Radium, Polonium

Użyj komendy by zamienić zaznaczony szablon na tabelkę Markdown.

Lista uporządkowana i nieuporządkowana

Możliwe jest tworzenie listy numerowanych oraz wypunktowanych. Wystarczy, że pierwszym znakiem linii będzie * lub - dla listy nieuporządkowanej oraz 1. dla listy uporządkowanej.

Użyj komendy by dodać listę uporządkowaną.

1. Lista numerowana
2. Lista numerowana

Użyj komendy by dodać listę nieuporządkowaną.

* Lista wypunktowana
* Lista wypunktowana
** Lista wypunktowana (drugi poziom)

Składnia Markdown

Edytor obsługuje składnię Markdown, która składa się ze znaków specjalnych. Dostępne komendy, jak formatowanie , dodanie tabelki lub fragmentu kodu są w pewnym sensie świadome otaczającej jej składni, i postarają się unikać uszkodzenia jej.

Dla przykładu, używając tylko dostępnych komend, nie możemy dodać formatowania pogrubienia do kodu wielolinijkowego, albo dodać listy do tabelki - mogłoby to doprowadzić do uszkodzenia składni.

W pewnych odosobnionych przypadkach brak nowej linii przed elementami markdown również mógłby uszkodzić składnie, dlatego edytor dodaje brakujące nowe linie. Dla przykładu, dodanie formatowania pochylenia zaraz po tabelce, mogłoby zostać błędne zinterpretowane, więc edytor doda oddzielającą nową linię pomiędzy tabelką, a pochyleniem.

Skróty klawiszowe

Skróty formatujące, kiedy w edytorze znajduje się pojedynczy kursor, wstawiają sformatowany tekst przykładowy. Jeśli w edytorze znajduje się zaznaczenie (słowo, linijka, paragraf), wtedy zaznaczenie zostaje sformatowane.

  • Ctrl+B - dodaj pogrubienie lub pogrub zaznaczenie
  • Ctrl+I - dodaj pochylenie lub pochyl zaznaczenie
  • Ctrl+U - dodaj podkreślenie lub podkreśl zaznaczenie
  • Ctrl+S - dodaj przekreślenie lub przekreśl zaznaczenie

Notacja Klawiszy

  • Alt+K - dodaj notację klawiszy

Fragment kodu bez oznacznika

  • Alt+C - dodaj pusty fragment kodu

Skróty operujące na kodzie i linijkach:

  • Alt+L - zaznaczenie całej linii
  • Alt+, Alt+ - przeniesienie linijki w której znajduje się kursor w górę/dół.
  • Tab/⌘+] - dodaj wcięcie (wcięcie w prawo)
  • Shit+Tab/⌘+[ - usunięcie wcięcia (wycięcie w lewo)

Dodawanie postów:

  • Ctrl+Enter - dodaj post
  • ⌘+Enter - dodaj post (MacOS)