Ist es möglich, eine DB-Sequenz für eine Spalte zu verwenden, die nicht der Bezeichner ist / nicht Teil eines zusammengesetzten Bezeichners ist ?
Ich verwende den Ruhezustand als JPA-Anbieter und habe eine Tabelle mit einigen Spalten, in denen Werte generiert werden (unter Verwendung einer Sequenz), obwohl sie nicht Teil des Bezeichners sind.
Ich möchte eine Sequenz verwenden, um einen neuen Wert für eine Entität zu erstellen, wobei die Spalte für die Sequenz NICHT (Teil) des Primärschlüssels ist:
@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;
}
}
Wenn ich das mache:
em.persist(new MyEntity());
Die ID wird generiert, aber die mySequenceVal
Eigenschaft wird auch von meinem JPA-Anbieter generiert.
Nur um die Dinge klar zu machen: Ich möchte, dass Hibernate den Wert für die mySequencedValue
Eigenschaft generiert . Ich weiß, dass Hibernate datenbankgenerierte Werte verarbeiten kann, aber ich möchte keinen Trigger oder etwas anderes als Hibernate selbst verwenden, um den Wert für meine Eigenschaft zu generieren. Wenn der Ruhezustand Werte für Primärschlüssel generieren kann, warum kann er dann nicht für eine einfache Eigenschaft generiert werden?
@GeneratedValue
Felder zuzulassen , die nicht id sind. Bitte stimmen Sie ab, um in 2.2 aufgenommen zu werden. Java.net/jira/browse/JPA_SPEC-113Ich fand, dass
@Column(columnDefinition="serial")
das perfekt funktioniert, aber nur für PostgreSQL. Für mich war dies die perfekte Lösung, da die zweite Entität eine "hässliche" Option ist.quelle
columnDefinition=
Bit weist Hiberate grundsätzlich an, nicht zu versuchen, die Spaltendefinition zu generieren, sondern stattdessen den von Ihnen angegebenen Text zu verwenden. Im Wesentlichen ist Ihre DDL für die Spalte buchstäblich nur name + columnDefinition. In diesem Fall (PostgreSQL)mycolumn serial
ist eine gültige Spalte in einer Tabelle.@Column(columnDefinition = "integer auto_increment")
@Column(insertable = false, updatable = false, columnDefinition="serial")
verhindert, dass der Ruhezustand versucht, Nullwerte einzufügen oder das Feld zu aktualisieren. Sie müssen dann die Datenbank erneut abfragen, um die generierte ID nach einer Einfügung zu erhalten, wenn Sie sie sofort verwenden müssen.Ich weiß, dass dies eine sehr alte Frage ist, aber sie zeigt sich zuerst in den Ergebnissen und jpa hat sich seit der Frage sehr verändert.
Der richtige Weg, dies jetzt zu tun, ist mit der
@Generated
Anmerkung. Sie können die Sequenz definieren, die Standardeinstellung in der Spalte auf diese Sequenz festlegen und die Spalte dann wie folgt zuordnen:quelle
Der Ruhezustand unterstützt dies definitiv. Aus den Dokumenten:
"Generierte Eigenschaften sind Eigenschaften, deren Werte von der Datenbank generiert werden. In der Regel müssen Anwendungen im Ruhezustand Objekte aktualisieren, die Eigenschaften enthalten, für die die Datenbank Werte generiert hat. Durch Markieren von Eigenschaften als generiert kann die Anwendung diese Verantwortung jedoch an den Ruhezustand delegieren. Wenn Hibernate ein SQL INSERT oder UPDATE für eine Entität ausgibt, die generierte Eigenschaften definiert hat, gibt es im Wesentlichen sofort eine Auswahl aus, um die generierten Werte abzurufen. "
Für Eigenschaften, die nur beim Einfügen generiert werden, sieht Ihre Eigenschaftszuordnung (.hbm.xml) folgendermaßen aus:
Für Eigenschaften, die beim Einfügen und Aktualisieren generiert wurden, sieht Ihre Eigenschaftszuordnung (.hbm.xml) folgendermaßen aus:
Leider kenne ich JPA nicht, daher weiß ich nicht, ob diese Funktion über JPA verfügbar gemacht wird (ich vermute möglicherweise nicht).
Alternativ sollten Sie in der Lage sein, die Eigenschaft von Einfügungen und Aktualisierungen auszuschließen und dann session.refresh (obj) "manuell" aufzurufen. nachdem Sie es eingefügt / aktualisiert haben, um den generierten Wert aus der Datenbank zu laden.
Auf diese Weise würden Sie die Eigenschaft von der Verwendung in Anweisungen zum Einfügen und Aktualisieren ausschließen:
Auch hier weiß ich nicht, ob JPA diese Hibernate-Funktionen verfügbar macht, aber Hibernate unterstützt sie.
quelle
Als Folge habe ich Folgendes zum Laufen gebracht:
quelle
SQLQuery sqlQuery = getSession().createSQLQuery("select NAMED_SEQ.nextval seq from dual"); sqlQuery.addScalar("seq", LongType.INSTANCE); return (Long) sqlQuery.uniqueResult();
Ich habe die Generierung von UUID (oder Sequenzen) mit Hibernate mithilfe von
@PrePersist
Anmerkungen korrigiert:quelle
Obwohl dies ein alter Thread ist, möchte ich meine Lösung teilen und hoffentlich ein Feedback dazu erhalten. Seien Sie gewarnt, dass ich diese Lösung nur mit meiner lokalen Datenbank in einem JUnit-Testfall getestet habe. Dies ist also bisher keine produktive Funktion.
Ich habe dieses Problem für mich gelöst, indem ich eine benutzerdefinierte Annotation namens Sequence ohne Eigenschaft eingeführt habe. Es ist nur eine Markierung für Felder, denen ein Wert aus einer inkrementierten Sequenz zugewiesen werden soll.
Mit dieser Anmerkung habe ich meine Entitäten markiert.
Um die Datenbank unabhängig zu halten, habe ich eine Entität namens SequenceNumber eingeführt, die den aktuellen Sequenzwert und die Inkrementgröße enthält. Ich habe den Klassennamen als eindeutigen Schlüssel ausgewählt, damit jede Entitätsklasse ihre eigene Sequenz erhält.
Der letzte und schwierigste Schritt ist ein PreInsertListener, der die Zuweisung der Sequenznummer übernimmt. Beachten Sie, dass ich Feder als Bohnenbehälter verwendet habe.
Wie Sie dem obigen Code entnehmen können, hat der Listener eine SequenceNumber-Instanz pro Entitätsklasse verwendet und reserviert einige Sequenznummern, die durch den incrementValue der SequenceNumber-Entität definiert sind. Wenn die Sequenznummern ausgehen, wird die SequenceNumber-Entität für die Zielklasse geladen und inkrementelle Werte für die nächsten Aufrufe reserviert. Auf diese Weise muss ich die Datenbank nicht jedes Mal abfragen, wenn ein Sequenzwert benötigt wird. Beachten Sie die StatelessSession, die geöffnet wird, um den nächsten Satz von Sequenznummern zu reservieren. Sie können nicht dieselbe Sitzung verwenden, in der die Zielentität derzeit beibehalten wird, da dies zu einer ConcurrentModificationException im EntityPersister führen würde.
Hoffe das hilft jemandem.
quelle
Wenn Sie postgresql verwenden
Und ich verwende im Frühjahr Boot 1.5.6
quelle
seq_order
und eine Referenz aus dem Feld erstellen ,nextval('seq_order'::regclass)
Ich bin in der gleichen Situation wie Sie und habe auch keine ernsthaften Antworten gefunden, ob es grundsätzlich möglich ist, mit JPA Nicht-ID-Eigenschaften zu generieren oder nicht.
Meine Lösung besteht darin, die Sequenz mit einer nativen JPA-Abfrage aufzurufen, um die Eigenschaft von Hand festzulegen, bevor sie beibehalten wird.
Dies ist nicht zufriedenstellend, funktioniert jedoch im Moment als Problemumgehung.
Mario
quelle
Ich habe diesen speziellen Hinweis in Sitzung 9.1.9 GeneratedValue-Annotation aus der JPA-Spezifikation gefunden: "[43] Tragbare Anwendungen sollten die GeneratedValue-Annotation nicht für andere persistente Felder oder Eigenschaften verwenden." Daher gehe ich davon aus, dass es nicht möglich ist, automatisch Werte für Nicht-Primärschlüsselwerte zu generieren, zumindest nicht einfach mit JPA.
quelle
Sieht so aus, als wäre der Thread alt, ich wollte nur meine Lösung hier hinzufügen (Verwenden von AspectJ - AOP im Frühjahr).
Die Lösung besteht darin, eine benutzerdefinierte Anmerkung
@InjectSequenceValue
wie folgt zu erstellen .Jetzt können Sie jedes Feld in der Entität mit Anmerkungen versehen, sodass der zugrunde liegende Feldwert (Long / Integer) zur Laufzeit mit dem nächsten Wert der Sequenz eingefügt wird.
Kommentieren Sie so.
Bisher haben wir das Feld markiert, das wir zum Einfügen des Sequenzwerts benötigen. Wir werden also untersuchen, wie der Sequenzwert in die markierten Felder eingefügt wird. Dazu wird der Punktschnitt in AspectJ erstellt.
Wir werden die Injektion kurz vor der
save/persist
Ausführung der Methode auslösen. Dies erfolgt in der folgenden Klasse.Jetzt können Sie jede Entität wie folgt mit Anmerkungen versehen.
quelle
"Ich möchte keinen Auslöser oder etwas anderes als den Ruhezustand selbst verwenden, um den Wert für mein Eigentum zu generieren."
Wie wäre es in diesem Fall, eine Implementierung von UserType zu erstellen, die den erforderlichen Wert generiert, und die Metadaten so zu konfigurieren, dass dieser UserType für die Persistenz der Eigenschaft mySequenceVal verwendet wird?
quelle
Dies ist nicht dasselbe wie die Verwendung einer Sequenz. Wenn Sie eine Sequenz verwenden, fügen Sie nichts ein oder aktualisieren nichts. Sie rufen einfach den nächsten Sequenzwert ab. Es sieht so aus, als würde der Ruhezustand dies nicht unterstützen.
quelle
Wenn Sie eine Spalte mit dem Typ UNIQUEIDENTIFIER haben und beim Einfügen eine Standardgenerierung benötigen, die Spalte jedoch nicht PK ist
In db wirst du haben
In diesem Fall definieren Sie keinen Generator für einen Wert, den Sie benötigen (dies erfolgt automatisch dank
columnDefinition="UNIQUEIDENTIFIER"
). Das gleiche können Sie für andere Spaltentypen versuchenquelle
Ich habe eine Problemumgehung für MySQL-Datenbanken mit @PostConstruct und JdbcTemplate in einer Spring-Anwendung gefunden. Es kann mit anderen Datenbanken durchgeführt werden, aber der Anwendungsfall, den ich vorstellen werde, basiert auf meinen Erfahrungen mit MySql, da es auto_increment verwendet.
Zuerst hatte ich versucht, eine Spalte mit der ColumnDefinition-Eigenschaft der @ Column-Annotation als auto_increment zu definieren, aber es funktionierte nicht, da die Spalte ein Schlüssel sein musste, um automatisch inkrementell zu sein, aber anscheinend wurde die Spalte nicht als definiert ein Index bis nach seiner Definition, was zu einem Deadlock führt.
Hier ist , wo ich mit der Idee der Schaffung der Säule ohne die auto_increment Definition kam, und das Hinzufügen es nach dem der Datenbank . Dies ist mithilfe der Annotation @PostConstruct möglich, die bewirkt, dass eine Methode direkt nach der Initialisierung der Beans durch die Anwendung in Verbindung mit der Aktualisierungsmethode von JdbcTemplate aufgerufen wird.
Der Code lautet wie folgt:
In meiner Entität:
In einer PostConstructComponent-Klasse:
quelle
Ich möchte neben der akzeptierten Lösung von @Morten Berg eine Alternative anbieten, die für mich besser funktioniert hat.
Dieser Ansatz ermöglicht es, das Feld mit dem tatsächlich gewünschten
Number
Typ zu definieren -Long
in meinem Anwendungsfall - anstelle vonGeneralSequenceNumber
. Dies kann nützlich sein, z. B. für die JSON (De-) Serialisierung.Der Nachteil ist, dass etwas mehr Datenbank-Overhead erforderlich ist.
Zuerst benötigen wir eine,
ActualEntity
in der wir dengenerated
Typ automatisch inkrementieren möchtenLong
:Als nächstes brauchen wir eine Hilfseinheit
Generated
. Ich habe es paketprivat neben platziertActualEntity
, um ein Implementierungsdetail des Pakets zu erhalten:Schließlich brauchen wir einen Platz zum Einhängen, bevor wir das speichern
ActualEntity
. Dort erstellen wir eineGenerated
Instanz und behalten sie bei . Dies liefert dann eineid
vom Typ generierte DatenbanksequenzLong
. Wir nutzen diesen Wert, indem wir ihn schreibenActualEntity.generated
.In meinem Anwendungsfall habe ich dies mithilfe eines Spring Data REST implementiert
@RepositoryEventHandler
, der unmittelbar vor demActualEntity
Fortbestehen des Get aufgerufen wird . Es sollte das Prinzip demonstrieren:Ich habe es nicht in einer realen Anwendung getestet, also genießen Sie es bitte mit Sorgfalt.
quelle
Ich war in einer Situation wie Sie (JPA / Hibernate-Sequenz für Nicht-@Id-Feld) und habe schließlich einen Trigger in meinem Datenbankschema erstellt, der beim Einfügen eine eindeutige Sequenznummer hinzufügt. Ich habe es einfach nie geschafft, mit JPA / Hibernate zu arbeiten
quelle
Nachdem ich Stunden verbracht hatte, half mir dies ordentlich, mein Problem zu lösen:
Für Oracle 12c:
Für H2:
Machen Sie auch:
quelle