Czy można użyć sekwencji DB dla jakiejś kolumny, która nie jest identyfikatorem / nie jest częścią identyfikatora złożonego ?
Używam hibernacji jako dostawcy jpa i mam tabelę, która ma kilka kolumn, w których są generowane wartości (przy użyciu sekwencji), chociaż nie są one częścią identyfikatora.
Chcę użyć sekwencji do utworzenia nowej wartości dla jednostki, w której kolumna sekwencji NIE jest (częścią) klucza podstawowego:
@Entity
@Table(name = "MyTable")
public class MyEntity {
//...
@Id //... etc
public Long getId() {
return id;
}
//note NO @Id here! but this doesn't work...
@GeneratedValue(strategy = GenerationType.AUTO, generator = "myGen")
@SequenceGenerator(name = "myGen", sequenceName = "MY_SEQUENCE")
@Column(name = "SEQ_VAL", unique = false, nullable = false, insertable = true, updatable = true)
public Long getMySequencedValue(){
return myVal;
}
}
Wtedy, kiedy to zrobię:
em.persist(new MyEntity());
identyfikator zostanie wygenerowany, ale mySequenceVal
właściwość zostanie również wygenerowana przez mojego dostawcę JPA.
Dla jasności: chcę, aby Hibernate generowało wartość dla mySequencedValue
nieruchomości. Wiem, że Hibernate może obsługiwać wartości generowane przez bazę danych, ale nie chcę używać wyzwalacza ani niczego innego niż sam Hibernate, aby wygenerować wartość dla mojej właściwości. Jeśli Hibernate może generować wartości dla kluczy podstawowych, dlaczego nie może generować prostej właściwości?
@GeneratedValue
na pola, które nie mają identyfikatora. Zagłosuj na dołączenie do wersji 2.2 java.net/jira/browse/JPA_SPEC-113Okazało się, że
@Column(columnDefinition="serial")
działa idealnie, ale tylko dla PostgreSQL. Dla mnie to było idealne rozwiązanie, bo druga rzecz to „brzydka” opcja.źródło
columnDefinition=
Bit w zasadzie mówi Hiberate, aby nie próbował generować definicji kolumny i zamiast tego używał podanego tekstu. Zasadniczo twój DDL dla kolumny będzie dosłownie po prostu nazwą + columnDefinition. W tym przypadku (PostgreSQL)mycolumn serial
jest poprawną kolumną w tabeli.@Column(columnDefinition = "integer auto_increment")
@Column(insertable = false, updatable = false, columnDefinition="serial")
zapobiegałem hibernacji przed próbą wstawiania wartości null lub aktualizowania pola. Następnie musisz ponownie wysłać zapytanie do bazy danych, aby uzyskać wygenerowany identyfikator po wstawieniu, jeśli chcesz go od razu użyć.Wiem, że jest to bardzo stare pytanie, ale zostało pokazane po raz pierwszy w wynikach, a jpa bardzo się zmienił od czasu pytania.
Właściwy sposób na zrobienie tego teraz polega na zastosowaniu
@Generated
adnotacji. Możesz zdefiniować sekwencję, ustawić wartość domyślną w kolumnie na tę sekwencję, a następnie zmapować kolumnę jako:źródło
Hibernate zdecydowanie to obsługuje. Z dokumentów:
„Właściwości generowane to właściwości, których wartości są generowane przez bazę danych. Zwykle aplikacje Hibernate potrzebowały do odświeżania obiektów zawierających właściwości, dla których baza danych generowała wartości. Oznaczanie właściwości jako wygenerowanych pozwala jednak aplikacji przekazać tę odpowiedzialność Hibernate. Zasadniczo za każdym razem, gdy Hibernate wydaje instrukcję SQL INSERT lub UPDATE dla jednostki, która ma zdefiniowane wygenerowane właściwości, natychmiast wydaje następnie polecenie wyboru w celu pobrania wygenerowanych wartości ”.
W przypadku właściwości wygenerowanych tylko przy wstawianiu mapowanie właściwości (.hbm.xml) wyglądałoby następująco:
Dla właściwości wygenerowanych przy wstawianiu i aktualizowaniu mapowania właściwości (.hbm.xml) wyglądałoby następująco:
Niestety nie znam JPA, więc nie wiem, czy ta funkcja jest ujawniona przez JPA (podejrzewam, że może nie)
Alternatywnie powinieneś móc wykluczyć właściwość z operacji wstawiania i aktualizacji, a następnie „ręcznie” wywołać session.refresh (obj); po wstawieniu / zaktualizowaniu go, aby załadować wygenerowaną wartość z bazy danych.
W ten sposób można wykluczyć właściwość z używania w instrukcjach insert i update:
Ponownie, nie wiem, czy JPA ujawnia te funkcje Hibernacji, ale Hibernate je obsługuje.
źródło
W ramach kontynuacji oto, jak to działa:
źródło
SQLQuery sqlQuery = getSession().createSQLQuery("select NAMED_SEQ.nextval seq from dual"); sqlQuery.addScalar("seq", LongType.INSTANCE); return (Long) sqlQuery.uniqueResult();
Naprawiłem generowanie UUID (lub sekwencji) w Hibernate za pomocą
@PrePersist
adnotacji:źródło
Chociaż jest to stary wątek, chcę podzielić się moim rozwiązaniem i mam nadzieję, że otrzymam na ten temat opinię. Ostrzegam, że testowałem to rozwiązanie tylko z moją lokalną bazą danych w jakimś przypadku testowym JUnit. Jak dotąd nie jest to wydajna funkcja.
Rozwiązałem ten problem, wprowadzając niestandardową adnotację o nazwie Sekwencja bez właściwości. To tylko znacznik dla pól, którym należy przypisać wartość z sekwencji zwiększonej.
Używając tej adnotacji, oznaczyłem moje encje.
Aby zachować niezależność bazy danych rzeczy, wprowadziłem jednostkę o nazwie SequenceNumber, która przechowuje bieżącą wartość sekwencji i rozmiar przyrostu. Wybrałem className jako unikalny klucz, więc każda klasa encji otrzyma własną sekwencję.
Ostatnim krokiem i najtrudniejszym jest PreInsertListener, który obsługuje przypisanie numeru sekwencji. Zauważ, że użyłem wiosny jako pojemnika na fasolę.
Jak widać z powyższego kodu, odbiornik użył jednej instancji SequenceNumber na klasę jednostki i rezerwuje kilka numerów sekwencyjnych zdefiniowanych przez jednostkę IncrementValue jednostki SequenceNumber. Jeśli zabraknie numerów sekwencyjnych, ładuje jednostkę SequenceNumber dla klasy docelowej i rezerwuje wartości IncrementValue dla następnych wywołań. W ten sposób nie muszę przesyłać zapytań do bazy danych za każdym razem, gdy potrzebna jest wartość sekwencji. Zwróć uwagę na StatelessSession, który jest otwierany w celu zarezerwowania następnego zestawu numerów sekwencyjnych. Nie można użyć tej samej sesji, w której jednostka docelowa jest obecnie utrwalana, ponieważ spowodowałoby to ConcurrentModificationException w EntityPersister.
Mam nadzieję, że to komuś pomoże.
źródło
Jeśli używasz postgresql
A ja używam w butach wiosennych 1.5.6
źródło
seq_order
i odnieść się do pola,nextval('seq_order'::regclass)
Działam w takiej samej sytuacji jak Ty i nie znalazłem też żadnych poważnych odpowiedzi, czy w zasadzie możliwe jest wygenerowanie właściwości bez identyfikatora za pomocą JPA, czy nie.
Moim rozwiązaniem jest wywołanie sekwencji za pomocą natywnego zapytania JPA w celu ręcznego ustawienia właściwości przed jej utrwaleniem.
Nie jest to satysfakcjonujące, ale na razie działa jako obejście.
Mario
źródło
Znalazłem tę szczególną uwagę w sesji 9.1.9 Adnotacja GeneratedValue ze specyfikacji JPA: „[43] Aplikacje przenośne nie powinny używać adnotacji GeneratedValue w innych trwałych polach lub właściwościach”. Zakładam więc, że nie jest możliwe automatyczne generowanie wartości dla wartości kluczy innych niż podstawowe, przynajmniej przy użyciu prostego JPA.
źródło
Wygląda na to, że wątek jest stary, chciałem tylko dodać tutaj moje rozwiązanie (wiosną przy użyciu AspectJ - AOP).
Rozwiązaniem jest utworzenie niestandardowej adnotacji
@InjectSequenceValue
w następujący sposób.Teraz możesz dodać adnotację do dowolnego pola w encji, tak aby wartość pola bazowego (Long / Integer) została wstrzyknięta w czasie wykonywania przy użyciu następnej wartości sekwencji.
Opisuj w ten sposób.
Do tej pory zaznaczyliśmy pole, które musimy wstrzyknąć wartość sekwencji, więc przyjrzymy się, jak wstrzyknąć wartość sekwencji do zaznaczonych pól, odbywa się to poprzez utworzenie cięcia punktowego w AspectJ.
Wstrzyknięcie wyzwolimy tuż przed wykonaniem
save/persist
metody, co odbywa się w poniższej klasie.Teraz możesz dodać adnotację do dowolnej jednostki, jak poniżej.
źródło
„Nie chcę używać wyzwalacza ani niczego innego niż sama Hibernacja do generowania wartości dla mojej nieruchomości”
W takim przypadku, co powiesz na utworzenie implementacji UserType, która generuje wymaganą wartość i skonfigurowanie metadanych tak, aby używały tego UserType do trwałości właściwości mySequenceVal?
źródło
To nie to samo, co użycie sekwencji. Używając sekwencji, niczego nie wstawiasz ani nie aktualizujesz. Po prostu pobierasz następną wartość sekwencji. Wygląda na to, że hibernacja go nie obsługuje.
źródło
Jeśli masz kolumnę typu UNIQUEIDENTIFIER i domyślną generację potrzebną do wstawiania, ale kolumna nie jest PK
W db będziesz mieć
W takim przypadku nie zdefiniujesz generatora dla wartości, której potrzebujesz (będzie to automatycznie dzięki
columnDefinition="UNIQUEIDENTIFIER"
). To samo możesz wypróbować dla innych typów kolumnźródło
Znalazłem obejście tego problemu w bazach danych MySql przy użyciu @PostConstruct i JdbcTemplate w aplikacji Spring. Może to być wykonalne z innymi bazami danych, ale przypadek użycia, który przedstawię, jest oparty na moich doświadczeniach z MySql, ponieważ używa on auto_increment.
Po pierwsze, próbowałem zdefiniować kolumnę jako auto_increment przy użyciu właściwości ColumnDefinition adnotacji @Column, ale nie działała ona, ponieważ kolumna musiała być kluczem, aby była automatycznie inkrementalna, ale najwyraźniej kolumna nie została zdefiniowana jako indeks, dopóki nie został zdefiniowany, powodując zakleszczenie.
W tym miejscu przyszedł mi do głowy pomysł stworzenia kolumny bez definicji auto_increment i dodania jej po utworzeniu bazy danych. Jest to możliwe dzięki adnotacji @PostConstruct, która powoduje wywołanie metody zaraz po zainicjowaniu komponentów bean przez aplikację, w połączeniu z metodą update JdbcTemplate.
Kod wygląda następująco:
W mojej jednostce:
W klasie PostConstructComponent:
źródło
Chcę zapewnić alternatywę obok przyjętego rozwiązania @Morten Berg, które działało lepiej dla mnie.
Takie podejście pozwala zdefiniować pole z faktycznie pożądanym
Number
typem -Long
w moim przypadku - zamiastGeneralSequenceNumber
. Może to być przydatne, np. Do serializacji JSON (de-).Wadą jest to, że wymaga trochę więcej narzutu na bazę danych.
Najpierw potrzebujemy elementu,
ActualEntity
w którym chcemy automatycznie zwiększaćgenerated
typLong
:Następnie potrzebujemy jednostki pomocniczej
Generated
. Umieściłem go obok pakietu prywatnegoActualEntity
, aby zachować szczegół implementacji pakietu:Na koniec potrzebujemy miejsca, w którym można podłączyć się tuż przed zapisaniem pliku
ActualEntity
. Tam tworzymy i utrzymujemyGenerated
instancję. To następnie zapewnia wygenerowaną sekwencję bazy danychid
typuLong
. Używamy tej wartości, pisząc ją doActualEntity.generated
.W moim przypadku zaimplementowałem to za pomocą Spring Data REST
@RepositoryEventHandler
, który jest wywoływany tuż przedActualEntity
utrwaleniem get. Powinien pokazywać zasadę:Nie testowałem tego w prawdziwej aplikacji, więc ciesz się ostrożnie.
źródło
Byłem w takiej sytuacji jak Ty (sekwencja JPA / Hibernacja dla pola innego niż @Id) i skończyło się na utworzeniu wyzwalacza w moim schemacie db, który dodaje unikalny numer sekwencyjny przy wstawianiu. Po prostu nigdy nie działało z JPA / Hibernate
źródło
Po spędzeniu godzin pomogło mi to w rozwiązaniu problemu:
W przypadku Oracle 12c:
Dla H2:
Wykonaj również:
źródło