Jak działa @Version
adnotacja w JPA?
Znalazłem różne odpowiedzi, których wyciąg jest następujący:
JPA używa pola wersji w jednostkach, aby wykrywać współbieżne modyfikacje tego samego rekordu magazynu danych. Gdy środowisko wykonawcze JPA wykryje próbę równoczesnej modyfikacji tego samego rekordu, zgłasza wyjątek od transakcji, która próbuje zatwierdzić jako ostatnia.
Ale nadal nie jestem pewien, jak to działa.
Również z następujących linii:
Należy rozważyć niezmienność pól wersji. Zmiana wartości pola ma niezdefiniowane wyniki.
Czy to oznacza, że powinniśmy zadeklarować nasze pole wersji jako final
?
java
jpa
jpa-annotations
Yatendra Goel
źródło
źródło
UPDATE myentity SET mycolumn = 'new value', version = version + 1 WHERE version = [old.version]
. Jeśli ktoś zaktualizował rekord,old.version
nie będzie już pasował do tego w bazie danych, a klauzula where uniemożliwi aktualizację. Będą to „zaktualizowane wiersze”0
, które JPA może wykryć, aby stwierdzić, że nastąpiła równoległa modyfikacja.Odpowiedzi:
Powiedzmy, że encja
MyEntity
ma opisanąversion
właściwość:Po aktualizacji pole z adnotacją
@Version
zostanie zwiększone i dodane doWHERE
klauzuli, coś takiego:Jeśli
WHERE
klauzula nie pasuje do rekordu (ponieważ ta sama jednostka została już zaktualizowana przez inny wątek), dostawca trwałości wyrzuci plikOptimisticLockException
.Nie, ale możesz rozważyć zabezpieczenie rozgrywającego, ponieważ nie powinieneś tego nazywać.
źródło
long
lub po prostu dzwonieniulongValue()
do niego. Potrzebujesz wyraźnychnull
czeków. Jawne zainicjowanie go samodzielnie Nie zrobiłbym tego, ponieważ to pole powinno być zarządzane przez dostawcę ORM. Myślę, że zostanie ustawiony na0L
przy pierwszym wstawieniu do bazy danych, więc jeśli jest ustawiony nanull
to, oznacza to, że ten rekord nie został jeszcze utrwalony.Long
niż prymitywulong
dla@Version
pola, ponieważSimpleJpaRepository.save(entity)
prawdopodobnie potraktuje pole wersji zerowej jako wskaźnik, że jednostka jest nowa. Jeśli jednostka jest nowa (co wskazuje wersja jest pusta), Spring wywołaem.persist(entity)
. Ale jeśli wersja ma wartość, to zadzwoniem.merge(entity)
. Z tego powodu pole wersji zadeklarowane jako typ opakowania powinno pozostać niezainicjowane.Chociaż odpowiedź @Pascal jest całkowicie poprawna, z mojego doświadczenia uważam, że poniższy kod jest pomocny w optymistycznym blokowaniu:
Czemu? Ponieważ:
@Version
jest przypadkowo ustawione nanull
.optlock
raczej niżversion
.Pierwszy punkt nie ma znaczenia, jeśli aplikacja używa tylko JPA do wstawiania danych do bazy danych, ponieważ dostawca JPA będzie wymuszał
0
dla@version
pola w czasie tworzenia. Ale prawie zawsze używane są również proste instrukcje SQL (przynajmniej podczas testów jednostkowych i integracyjnych).źródło
u_lmod
(ostatnia modyfikacja użytkownika), gdzie użytkownik może być człowiekiem lub zdefiniowanym (zautomatyzowanym) procesem / jednostką / aplikacją (więc dodatkowe przechowywanie równieżu_lmod_id
może mieć sens). (Moim zdaniem jest to bardzo podstawowy biznesowy meta-atrybut). Zwykle przechowuje swoją wartość jako DATA (CZAS) (co oznacza informacje o roku ... milisach) w UTC (jeśli „lokalizacja” strefy czasowej edytora jest ważna do przechowywania, to wraz ze strefą czasową). dodatkowo bardzo przydatne w scenariuszach synchronizacji DWH.Za każdym razem, gdy jednostka jest aktualizowana w bazie danych, pole wersji zostanie zwiększone o jeden. Każda operacja aktualizująca jednostkę w bazie danych zostanie dołączona
WHERE version = VERSION_THAT_WAS_LOADED_FROM_DATABASE
do jej zapytania.Sprawdzając wiersze operacji, których dotyczy problem, struktura jpa może upewnić się, że nie nastąpiła równoczesna modyfikacja między ładowaniem a utrwalaniem jednostki, ponieważ zapytanie nie znajdzie jednostki w bazie danych, gdy jej numer wersji został zwiększony między ładowaniem a utrwalaniem.
źródło
Wersja używana w celu zapewnienia, że tylko jedna aktualizacja na raz. Dostawca JPA sprawdzi wersję, jeśli oczekiwana wersja już się zwiększy, to ktoś inny już zaktualizuje jednostkę, więc zostanie wyrzucony wyjątek.
Tak więc aktualizacja wartości jednostki byłaby bezpieczniejsza i bardziej optymistyczna.
Jeśli wartość zmienia się często, możesz rozważyć nieużywanie pola wersji. Na przykład „jednostka, która ma pole licznika, które będzie się zwiększać za każdym razem, gdy otwierana jest strona internetowa”
źródło
Dodam trochę więcej informacji.
JPA zarządza wersją pod maską za Ciebie, jednak nie robi tego, gdy aktualizujesz rekord za pośrednictwem
JPAUpdateClause
, w takich przypadkach musisz ręcznie dodać przyrost wersji do zapytania.To samo można powiedzieć o aktualizacji przez JPQL, tj. Nie jest to prosta zmiana encji, ale polecenie aktualizacji do bazy danych, nawet jeśli odbywa się to przez hibernację
Pedro
źródło
JPAUpdateClause
?