Wszyscy znamy domyślne zachowanie Hibernate'a podczas używania @SequenceGenerator
- zwiększa rzeczywistą sekwencję bazy danych o jeden , pomnóż tę wartość o 50 ( allocationSize
wartość domyślna ) - a następnie używa tej wartości jako identyfikatora jednostki.
Jest to nieprawidłowe zachowanie i sprzeczne ze specyfikacją, która mówi:
ocationSize - (opcjonalne) kwota do zwiększenia podczas przydzielania numerów sekwencyjnych z sekwencji.
Żeby było jasne: nie przejmuję się lukami między wygenerowanymi identyfikatorami.
Zależy mi na identyfikatorach, które nie są zgodne z sekwencją bazową bazy danych. Na przykład: każda inna aplikacja (która np. Używa zwykłego JDBC) może chcieć wstawić nowe wiersze pod identyfikatorami uzyskanymi z sekwencji - ale wszystkie te wartości mogą być już wykorzystane przez Hibernate! Szaleństwo.
Czy ktoś zna jakieś rozwiązanie tego problemu (bez ustawiania, allocationSize=1
a tym samym obniżania wydajności)?
EDYCJA:
Aby wszystko było jasne. Jeśli ostatni wstawiony rekord miał ID = 1
, to HB użyje wartości 51, 52, 53...
dla swoich nowych jednostek, ALE w tym samym czasie: wartość sekwencji w bazie danych zostanie ustawiona na 2
. Co może łatwo prowadzić do błędów, gdy inne aplikacje używają tej sekwencji.
Z drugiej strony: specyfikacja mówi (w moim rozumieniu), że sekwencja bazy danych powinna była być ustawiona na, 51
a tymczasem HB powinien używać wartości z zakresu 2, 3 ... 50
AKTUALIZACJA:
Jak Steve Ebersole wspomniał poniżej: zachowanie opisane przeze mnie (a także najbardziej intuicyjne dla wielu) można włączyć, ustawiając hibernate.id.new_generator_mappings=true
.
Dziękuję wam wszystkim.
UPDATE 2:
Dla przyszłych czytelników poniżej znajduje się działający przykład.
@Entity
@Table(name = "users")
public class User {
@Id
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "USERS_SEQ")
@SequenceGenerator(name = "USERS_SEQ", sequenceName = "SEQUENCE_USERS")
private Long id;
}
persistence.xml
<persistence-unit name="testPU">
<properties>
<property name="hibernate.id.new_generator_mappings" value="true" />
</properties>
</persistence-unit>
save
musi zapytać bazę danych o następną wartość sekwencji.SequenceGenerator
Hibernate będzie wysyłać zapytania do bazy danych tylko wtedy, gdyallocationsize
skończy się ilość identyfikatorów określonych przez . Jeśli to skonfigurujesz,allocationSize = 1
to jest to powód, dla którego Hibernacja wysyła zapytanie do DB dla każdej wstawki. Zmień tę wartość i gotowe.hibernate.id.new_generator_mappings
ustawienie jest naprawdę ważne. Mam nadzieję, że jest to ustawienie domyślne, że nie muszę spędzać tyle czasu na badaniu, dlaczego numer id zwariował.Odpowiedzi:
Żeby było absolutnie jasne ... to, co opisujesz, w żaden sposób nie koliduje ze specyfikacją. Specyfikacja mówi o wartościach przypisywanych przez Hibernate do twoich encji, a nie o wartościach faktycznie przechowywanych w sekwencji bazy danych.
Istnieje jednak opcja uzyskania zachowania, którego szukasz. Najpierw zobacz moją odpowiedź na temat Czy istnieje sposób dynamicznego wybierania strategii @GeneratedValue za pomocą adnotacji JPA i Hibernacji? To da ci podstawy. Dopóki jesteś skonfigurowany do używania tego SequenceStyleGeneratora, Hibernate będzie interpretować
allocationSize
przy użyciu „optymalizatora puli” w SequenceStyleGenerator. „Optymalizator puli” jest przeznaczony do użytku z bazami danych, które umożliwiają opcję „inkrementacji” tworzenia sekwencji (nie wszystkie bazy danych obsługujące sekwencje obsługują przyrost). W każdym razie przeczytaj o różnych strategiach optymalizacji.źródło
org.hibernate.id.enhanced.SequenceStyleGenerator
. Zaskoczyłeś mnie.allocationSize=1
Jest to mikro optymalizacja przed otrzymaniem zapytania Hibernate stara się przypisać wartość z zakresu przydzielenia rozmiaru, a więc stara się unikać zapytania bazy danych o sekwencję. Ale to zapytanie będzie wykonywane za każdym razem, jeśli ustawisz je na 1. Nie robi to żadnej różnicy, ponieważ jeśli do Twojej bazy danych ma dostęp inna aplikacja, spowoduje to problemy, jeśli ten sam identyfikator będzie używany w międzyczasie przez inną aplikację.Następna generacja Id sekwencji jest oparta na rozmiarze przydzielenia.
Domyślnie jest
50
to za dużo. Pomoże to również tylko wtedy, gdy będziesz mieć blisko50
rekordów w jednej sesji, które nie są utrwalone i które zostaną utrwalone przy użyciu tej konkretnej sesji i transakcji.Dlatego zawsze powinieneś używać
allocationSize=1
podczas używaniaSequenceGenerator
. Podobnie jak w przypadku większości bazowych baz danych, sekwencja jest zawsze zwiększana o1
.źródło
allocationSize=1
Hibernate podczas każdejsave
operacji trzeba zrobić podróż do bazy danych w celu uzyskania nowej wartości ID.allocationSize
więc stara się unikać zapytania bazy danych o sekwencję. Ale to zapytanie będzie wykonywane za każdym razem, jeśli ustawisz je na 1. Nie robi to żadnej różnicy, ponieważ jeśli twoja baza danych jest używana przez jakąś inną aplikację, spowoduje to problemy, jeśli ten sam identyfikator będzie w międzyczasie używany przez inną aplikacjęSteve Ebersole i inni członkowie,
Czy mógłbyś uprzejmie wyjaśnić powód dla identyfikatora z większą luką (domyślnie 50)? Używam Hibernate 4.2.15 i znalazłem następujący kod w kasecie org.hibernate.id.enhanced.OptimizerFactory.
Za każdym razem, gdy trafia do wnętrza instrukcji if, wartość hi znacznie się zwiększa. Tak więc mój identyfikator podczas testowania z częstym restartem serwera generuje następujące identyfikatory sekwencji:
1, 2, 3, 4, 19, 250, 251, 252, 400, 550, 750, 751, 752, 850, 1100, 1150.
Wiem, że już powiedziałeś, że nie jest to sprzeczne ze specyfikacją, ale uważam, że będzie to bardzo nieoczekiwana sytuacja dla większości programistów.
Każdy wkład będzie bardzo pomocny.
Jihwan
UPDATE: ne1410s: Dzięki za edycję.
cfrick: OK. Zrobię to. To był mój pierwszy wpis tutaj i nie byłem pewien, jak go użyć.
Teraz lepiej zrozumiałem, dlaczego maxLo zostało użyte do dwóch celów: ponieważ hibernacja wywołuje sekwencję DB raz, zwiększaj identyfikator na poziomie Java i zapisuj go w bazie danych, wartość identyfikatora poziomu Java powinna uwzględniać, ile zostało zmienione bez wywoływania sekwencja DB, gdy wywoła sekwencję następnym razem.
Na przykład identyfikator sekwencji wynosił 1 w danym momencie, a hibernacja wprowadzono 5, 6, 7, 8, 9 (z przydziałem wielkości = 5). Następnym razem, gdy otrzymamy kolejny numer sekwencji, DB zwraca 2, ale hibernacja musi używać 10, 11, 12 ... Dlatego właśnie "hi = lastSourceValue.copy (). MultiplyBy (maxLo + 1)" jest używany do uzyskania następnego id 10 z 2 zwróconych z sekwencji DB. Wydaje się, że przeszkadzało tylko częste ponowne uruchamianie serwera i to był mój problem z większą luką.
Tak więc, kiedy używamy SEQUENCE ID, wstawiony identyfikator w tabeli nie będzie zgodny z numerem SEQUENCE w DB.
źródło
Po przekopaniu się do hibernacji kodu źródłowego i poniższej konfiguracji przechodzi do bazy danych Oracle po następną wartość po 50 wstawieniach. Zrób więc przyrost INST_PK_SEQ o 50 przy każdym wywołaniu.
Hibernate 5 jest używany do poniższej strategii
Sprawdź również poniżej http://docs.jboss.org/hibernate/orm/5.1/userguide/html_single/Hibernate_User_Guide.html#identifiers-generators-sequence
źródło
allocationSize
aniinitialValue
globalnie dla wszystkich podmiotów (chyba że używasz tylko jednego generatora, ale IMHO nie jest bardzo czytelny).Ja też napotkałem ten problem w Hibernate 5:
Oto ostrzeżenie takie jak to:
Następnie zmieniłem mój kod na SequenceStyleGenerator:
To rozwiązało moje dwa problemy:
źródło
Sprawdziłbym DDL pod kątem sekwencji w schemacie. Implementacja JPA odpowiada tylko za stworzenie sekwencji o odpowiednim rozmiarze alokacji. Dlatego jeśli rozmiar alokacji wynosi 50, sekwencja musi mieć przyrost o 50 w swoim DDL.
Ten przypadek może zwykle wystąpić podczas tworzenia sekwencji o rozmiarze alokacji 1, a następnie później konfigurowanej na rozmiar alokacji 50 (lub domyślny), ale sekwencja DDL nie jest aktualizowana.
źródło
ALTER SEQUENCE ... INCREMENTY BY 50;
niczego nie rozwiąże, ponieważ problem nadal pozostaje ten sam. Wartość sekwencji nadal nie odzwierciedla rzeczywistych identyfikatorów jednostek.