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

Pytanie po pewien szczegół działnia generatorów PK (@GeneratedValue)
P1
  • Rejestracja:ponad 14 lat
  • Ostatnio:ponad 12 lat
  • Postów:150
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

Kopiuj
 
@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".

__krzysiek85
  • Rejestracja:ponad 18 lat
  • Ostatnio:ponad 9 lat
  • Postów:1019
0

Registered Linux user #456405 | SCJP 6 | SCWCD 5 | SCBCD 5
P1
  • Rejestracja:ponad 14 lat
  • Ostatnio:ponad 12 lat
  • Postów:150
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ę:

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

To to samo działanie:

Kopiuj
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).

edytowany 2x, ostatnio: Pierce111
Koziołek
Moderator
  • Rejestracja:około 18 lat
  • Ostatnio:19 dni
  • Lokalizacja:Stacktrace
  • Postów:6821
0

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


Sięgam tam, gdzie wzrok nie sięga… a tam NullPointerException
P1
  • Rejestracja:ponad 14 lat
  • Ostatnio:ponad 12 lat
  • Postów:150
0

int

Koziołek
Moderator
  • Rejestracja:około 18 lat
  • Ostatnio:19 dni
  • Lokalizacja:Stacktrace
  • Postów:6821
0

Zmień na Integer i zobacz co się stanie.


Sięgam tam, gdzie wzrok nie sięga… a tam NullPointerException
P1
  • Rejestracja:ponad 14 lat
  • Ostatnio:ponad 12 lat
  • Postów:150
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ć.

edytowany 2x, ostatnio: Pierce111
Koziołek
Moderator
  • Rejestracja:około 18 lat
  • Ostatnio:19 dni
  • Lokalizacja:Stacktrace
  • Postów:6821
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?


Sięgam tam, gdzie wzrok nie sięga… a tam NullPointerException
Wibowit
A co jest bezpieczniejszego w sekwencji? Rozumiem, że chodzi ci o to, żeby nie używać autoincrement, tak?
Koziołek
sekwencje mają bardzo fajne właściwości jeśli chodzi o wycofywanie transakcji (olewają to). Dzięki temu nie trzeba w razie czego wycofywać zarówno zmian w głównej tabeli jak i w tabeli identyfikatorów. IMO lepiej jest wycofać jedna tabelę mniej.
P1
  • Rejestracja:ponad 14 lat
  • Ostatnio:ponad 12 lat
  • Postów:150
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 .

edytowany 2x, ostatnio: Pierce111
Koziołek
Moderator
  • Rejestracja:około 18 lat
  • Ostatnio:19 dni
  • Lokalizacja:Stacktrace
  • Postów:6821
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.


Sięgam tam, gdzie wzrok nie sięga… a tam NullPointerException
P1
  • Rejestracja:ponad 14 lat
  • Ostatnio:ponad 12 lat
  • Postów:150
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 ...........

Zarejestruj się i dołącz do największej społeczności programistów w Polsce.

Otrzymaj wsparcie, dziel się wiedzą i rozwijaj swoje umiejętności z najlepszymi.