Pytanie po pewien szczegół działnia generatorów PK (@GeneratedValue)

0

Myślałem że znam już całkowicie temat generatorów PK w JPA ale właśnie naszła mnie pewna wątpliwość na którą nie mogę znaleźć odpowiedzi w dokumentacji. Załóżmy że ma klasę encji z PK typu int. Domyślna wartość dla tego pola jest jeden

 
@Id
int my_pk = 1

Jeżeli nie zastosuje strategi generowania PK przez jakiś typ generatora to numery jakie podam do pola "my_pk" obiektów encji będą (po ich persistowaniu lub mergowaniu) numerami PK tworzonych w rekordów w tabeli. Jak tworzę jakiś obiekt encji to standardowo metoda "merge" po numerze pola "my_pk" tej encji musi rozpoznać czy rekord już istnieje (wtedy wiąże encję z tym rekordem) lub czy go jeszcze nie ma w tabeli (rekord zostanie stworzony przy committowaniu). Podobnie jest z "persist()" które tworzy nowy obiekt dla encji gdy taki rekord nie istnieje lub wyrzuca Exception gdy jednak jest (przy comittowaniu).
Ale jak to działa przy zastosowaniu Generatora wartości PK (IDENTITY, SEQUENCE lub TABLE ). Załóżmy ze moja tabela ma w bazie am jż kilka pierwszych rekordów (1,2,3,4). Gdy tworzę nowy obiekt encji jego wartośc pola PK ("my_pk") równa się domyślnemu 1.
JPA provider powinien ją uznać ten obiekt encji za odpowiednik istniejącego rekordu (z nr 1) i dla "merge()" powinien związać tą encja z istniejącym rekordem a dla persist powinien wyrzucić EntityExistsException. Jednak tak się nie dzieje i encje traktowane są jak nowe które należy dodać do bazy w postaci nowych rekordów. Jak JPA provider dokonuje tutaj tego rozpoznania jaki obiekt encji jest nowy?
Jak widać nie robi tego po polu "my_pk".

0

Dzięki za ten materiał ale on nie wyjaśnia problemu bo porusza trochę inne kwestie.
Moim JPA providerem jest JBoss. Mam stworzoną klasę encji Client z polem pk "id". Domyślna wartość "id" przy twozreniuobiektu encji równa się np. 0.
Jeżeli klasa nie ma opcji (adnotacji) GeneratedValue (nie korzysta z niej) to wygenerowanie w metodzie EJB obiektu
Client cli_1 = new Client();
a potem mergowanie go do Persistence Context spowoduje podłączenie tego obiektu encji do istniejącego już rekordu o numerze id = 0 w mojej tabeli Client w bazie danych. Jeżeli zmienię teraz jakąś wartość pola w tym obiekcie encji (np. nazwisko) to po comittowaniu transackji z metody EJB rekord w bazie o numerze 0 zostanie zmieniony.
Jeżeli do tej samej klasy encji zastosuję:

@Id
  @GeneratedValue(strategy=TABLE, generator="CLIENT_GEN")

To to samo działanie:

Client cli_1 = new  Client();  // tworzony jest obiekt encji również o numerze id =0
 em.merge(cli_1);

spowoduje dodanie zupełnie nowego rekordu do tabeli w bazie danych z numerem id pobranym w tabeli.

Mamy te same komendy a zupełnie różne reakcje.
Sprawdziłem to na przykładach dla różnych wartości domyślnych id (1,2) żeby nie było ze 0 jest wrażliwe

Działam tylko na jednym EntityMangerze (odnośnie artykułu).

0

A tak z ciekawości czy identyfikator jest typu int czy Integer....

0

int

0

Zmień na Integer i zobacz co się stanie.

0

Myślisz o tym żeby zamienić na Integer i zostawić inicjację domyślna pola "id" na null dla nowych obiektów encji? Null może coś tu zmienić.

0

Jeżeli masz generator to silnik ORM widząc null pobierze kolejną wartość z generatora. Jeżeli będzie widział not-null to nie będzie sięgał do tabeli z generatorem. BTW czemu potrzebujesz generatora opartego o tabelę? Nie lepiej użyć bezpieczniejszego - sekwencji?

0

Na początku nie zajarzyłem i zrobiłem test dla Integera ale z przypisywaną wartością domyślną =Integer.valueOf(1); I działał dokładnie tak samo jak dla int - dwa rózne zachowania w zależności czy jest tabela włączona czy nie. Dopiero wtedy zajarzyłem że chodzi ci o tego nulla : ) .
Używam tabeli bo w MySQL nie ma sekwencji a poza tym słyszałem że są najbezpieczniejsze bo całkowicie przenośne miedzy bazami danych.
No ale widać że jest jakaś luka. Czyli co, nie ma wytłumaczenia tylko trzeba używać Integera z przypisywaniem nulla?
Zrobię jeszcze teścik dla tego nulla.

Ale, ale ....
(odnośnie twojego niesięgania do tabeli gdy nie jest null)
jak napisałem dla Integera działało jak dla int'a czyli jak była tabela właczona a encja miała przypisany obiekt Integer w "id" = 1 to i tak sięgał do tabeli i tworzył nowy obiekt poprawka: rekord .

0

Pamiętaj o jednym w zależności od tego jak generowane są wartości Id różnie może działać merge - inaczej wpinać obiekt do kontekstu. W ogólności nie powinieneś odczuwać różnicy, ale jak wiadomo są wyjątki.

Co do sekwencji to rzeczywiście są średnio przenośne, ale bardzo bezpieczne. Osobiście unikam tworzenia identyfikatorów w oparciu o sztuczne wartości. Dzięki temu nie mam problemów.

0

Dla inicjacji domyślnej pola PK "id" nullem dla włączonej tabeli mergowanie nowego obiektu encji doda nowy rekord na podstawie tabeli. Przy wyłączonej tabeli mergowanie takiego obiektu encji wyrzuci błąd braku wartości PK. Zachowanie przewidywalne.

Czyli wypisane wyżej zachowanie trzeba traktować jako lukę w działaniu JPA providera i tyle ...........

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