Ruhezustand, @SequenceGenerator und Zuordnungsgröße

117

Wir alle kennen das Standardverhalten von Hibernate bei der Verwendung @SequenceGenerator- es erhöht die reale Datenbanksequenz um eins , multipliziert diesen Wert um 50 (Standardwert allocationSize) - und verwendet diesen Wert dann als Entitäts-ID.

Dies ist ein falsches Verhalten und widerspricht der Spezifikation, die besagt:

Zuordnungsgröße - (Optional) Der Betrag, um den beim Zuweisen von Sequenznummern aus der Sequenz erhöht werden soll.

Um es klar auszudrücken: Ich kümmere mich nicht um Lücken zwischen generierten IDs.

Ich interessiere mich für IDs, die nicht mit der zugrunde liegenden Datenbanksequenz übereinstimmen . Beispiel: Jede andere Anwendung (die z. B. einfaches JDBC verwendet) möchte möglicherweise neue Zeilen unter IDs einfügen, die aus der Sequenz erhalten wurden. Alle diese Werte werden jedoch möglicherweise bereits von Hibernate verwendet. Wahnsinn.

Kennt jemand eine Lösung für dieses Problem (ohne die allocationSize=1Leistung einzustellen und damit zu beeinträchtigen)?

EDIT:
Um die Dinge klar zu machen. Wenn der zuletzt eingefügte Datensatz die ID = hatte 1, verwendet HB Werte 51, 52, 53...für seine neuen Entitäten, ABER gleichzeitig: Der Wert der Sequenz in der Datenbank wird auf gesetzt 2. Dies kann leicht zu Fehlern führen, wenn andere Anwendungen diese Sequenz verwenden.

Auf der anderen Seite: Die Spezifikation besagt (nach meinem Verständnis), dass die Datenbanksequenz auf eingestellt sein sollte 51und in der Zwischenzeit sollte HB Werte aus dem Bereich verwenden 2, 3 ... 50


UPDATE:
Wie Steve Ebersole unten erwähnte: Das von mir beschriebene (und für viele auch intuitivste) Verhalten kann durch Einstellen aktiviert werden hibernate.id.new_generator_mappings=true.

Danke an alle.

UPDATE 2:
Für zukünftige Leser finden Sie unten ein funktionierendes Beispiel.

@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>
G. Demecki
quelle
2
"Ohne die Zuordnung von Zuordnungsgröße = 1 und damit die Leistung zu beeinträchtigen" Warum wird die Leistung beeinträchtigt, wenn Sie sie auf 1 setzen?
Sheidaei
3
@sheidaei siehe kann Kommentar unten :-) Dies liegt daran, dass jeder savedie Datenbank nach dem nächsten Wert der Sequenz abfragen muss.
G. Demecki
Vielen Dank, dass Sie vor dem gleichen Problem standen. Zuerst habe ich bei jedem @SequenceGenerator Zuordnungsgröße = 1 hinzugefügt. Verwenden Sie hibernate.id.new_generator_mappings = true, um dies zu verhindern. Obwohl JPA immer noch die Datenbank
abfragt
1
Mit SequenceGeneratorHibernate wird die Datenbank nur abgefragt, wenn die Anzahl der durch angegebenen IDs erschöpft allocationsizeist. Wenn Sie einrichten, allocationSize = 1ist dies der Grund, warum der Ruhezustand die Datenbank für jede Einfügung abfragt. Ändern Sie diesen Wert, und Sie sind fertig.
G. Demecki
1
Vielen Dank! Die hibernate.id.new_generator_mappingsEinstellung ist wirklich wichtig. Ich würde hoffen, dass es die Standardeinstellung ist, dass ich nicht so viel Zeit damit verbringen muss zu recherchieren, warum die ID-Nummer wild wird.
LeOn - Han Li

Antworten:

43

Um ganz klar zu sein ... was Sie beschreiben , widerspricht in keiner Weise der Spezifikation. Die Spezifikation bezieht sich auf die Werte, die Hibernate Ihren Entitäten zuweist, nicht auf die Werte, die tatsächlich in der Datenbanksequenz gespeichert sind.

Es besteht jedoch die Möglichkeit, das gewünschte Verhalten zu erhalten. Siehe zuerst meine Antwort auf Gibt es eine Möglichkeit, eine @ GeneratedValue-Strategie mithilfe von JPA-Anmerkungen und Ruhezustand dynamisch auszuwählen? Das gibt Ihnen die Grundlagen. Solange Sie für die Verwendung dieses SequenceStyleGenerator eingerichtet sind, interpretiert Hibernate allocationSizeden "Pooled Optimizer" im SequenceStyleGenerator. Der "gepoolte Optimierer" ist für die Verwendung mit Datenbanken vorgesehen, die eine "Inkrement" -Option beim Erstellen von Sequenzen ermöglichen (nicht alle Datenbanken, die Sequenzen unterstützen, unterstützen ein Inkrement). Informieren Sie sich dort über die verschiedenen Optimierungsstrategien.

Steve Ebersole
quelle
Danke Steve! Die beste Antwort. Auch dein anderer Beitrag war hilfreich.
G. Demecki
4
Mir ist auch aufgefallen, dass Sie Mitautor von sind org.hibernate.id.enhanced.SequenceStyleGenerator. Du hast mich überrascht.
G. Demecki
22
Überrascht dich wie? Ich bin der Hauptentwickler von Hibernate. Ich habe viele Hibernate-Klassen geschrieben / mitgeschrieben;)
Steve Ebersole
Nur für das Protokoll. DB-Sequenz Inkremente sollten vermieden werden, um große Lücken zu vermeiden. DB-Sequenz wird mit ZuordnungSize multipliziert, wenn der ID-Cache leer ist. Weitere Details stackoverflow.com/questions/5346147/…
Olcay Tarazan
1
Eine Möglichkeit, den global verwendeten "Optimierer" zu ändern, besteht darin, Ihren Optionen für den Ruhezustand Folgendes hinzuzufügen: serviceBuilder.applySetting ("hibernate.id.optimizer.pooled.preferred", LegacyHiLoAlgorithmOptimizer.class.getName ()); Anstelle des LegacyHiLoAlgorithOptimizer können Sie eine beliebige Optimierungsklasse auswählen, die zum Standard wird. Dies sollte es einfacher machen, das gewünschte Verhalten als Standard beizubehalten, ohne alle Anmerkungen zu ändern. Achten Sie außerdem auf die Optimierer "gepoolt" und "hilo": Diese liefern ungerade Ergebnisse, wenn Ihr Sequenzwert bei 0 beginnt und negative IDs verursacht.
Fjalvingh
17

allocationSize=1Es handelt sich um eine Mikrooptimierung vor dem Abrufen der Abfrage. Hibernate versucht, einen Wert im Bereich von assignSize zuzuweisen, und versucht daher, das Abfragen der Datenbank nach Sequenzen zu vermeiden. Diese Abfrage wird jedoch jedes Mal ausgeführt, wenn Sie sie auf 1 setzen. Dies macht kaum einen Unterschied, da beim Zugriff auf Ihre Datenbank durch eine andere Anwendung Probleme auftreten, wenn dieselbe ID zwischenzeitlich von einer anderen Anwendung verwendet wird.

Die nächste Generation der Sequenz-ID basiert auf Zuordnungsgröße.

Auf jeden Fall wird es als 50zu viel gehalten. Es ist auch nur hilfreich, wenn Sie 50in einer Sitzung nahezu Datensätze haben, die nicht beibehalten werden und die mithilfe dieser bestimmten Sitzung und Übertragung beibehalten werden.

Daher sollten Sie immer allocationSize=1während der Verwendung verwenden SequenceGenerator. Wie bei den meisten zugrunde liegenden Datenbanken wird die Reihenfolge immer um erhöht 1.

Amit Deshpande
quelle
12
Nichts mit Leistung zu tun? Bist du dir wirklich sicher? Mir wurde beigebracht, dass mit allocationSize=1Hibernate bei jeder saveOperation die Reise zur Datenbank durchgeführt werden muss, um einen neuen ID-Wert zu erhalten.
G. Demecki
2
Es handelt sich um eine Mikrooptimierung vor dem Abrufen von Abfragen. Hibernate versucht, Werte im Bereich von zuzuweisen, allocationSizeund versucht daher, das Abfragen der Datenbank nach Sequenzen zu vermeiden. Diese Abfrage wird jedoch jedes Mal ausgeführt, wenn Sie sie auf 1 setzen. Dies macht kaum einen Unterschied, da beim Zugriff auf Ihre Datenbank durch eine andere Anwendung Probleme auftreten, wenn dieselbe ID zwischenzeitlich von einer anderen Anwendung verwendet wird
Amit Deshpande,
Und ja, es ist völlig anwendungsspezifisch, ob eine Zuordnungsgröße von 1 echte Auswirkungen auf die Leistung hat. In einem Mikro-Benchmark wird sich dies natürlich immer als enorme Auswirkung herausstellen. Das ist das Problem bei den meisten Benchmarks (Mikro oder andere), sie sind einfach nicht realistisch. Und selbst wenn sie komplex genug sind, um etwas realistisch zu sein, müssen Sie sich noch ansehen, wie nahe der Benchmark an Ihrer tatsächlichen Anwendung liegt, um zu verstehen, wie anwendbar die Benchmark-Ergebnisse auf die Ergebnisse sind, die Sie in Ihrer App sehen würden. Lange Rede, kurzer Sinn ... testen Sie es selbst
Steve Ebersole
2
OK. Alles ist anwendungsspezifisch, nicht wahr? Wenn es sich bei Ihrer Anwendung um eine schreibgeschützte Anwendung handelt, ist die Auswirkung der Verwendung der Zuordnungsgröße 1000 oder 1 absolut 0. Andererseits sind solche Dinge Best Practices. Wenn Sie die gesammelten Best Practices nicht respektieren und die kombinierte Auswirkung ist, wird Ihre Anwendung träge. Ein anderes Beispiel wäre das Starten einer Transaktion, wenn Sie absolut keine benötigen.
Hasan Ceylan
1

Steve Ebersole & andere Mitglieder,
würden Sie bitte den Grund für eine ID mit einer größeren Lücke erklären (standardmäßig 50)? Ich verwende Hibernate 4.2.15 und habe den folgenden Code in org.hibernate.id.enhanced.OptimizerFactory cass gefunden.

if ( lo > maxLo ) {
   lastSourceValue = callback.getNextValue();
   lo = lastSourceValue.eq( 0 ) ? 1 : 0;
   hi = lastSourceValue.copy().multiplyBy( maxLo+1 ); 
}  
value = hi.copy().add( lo++ );

Immer wenn es auf die Innenseite der if-Anweisung trifft, wird der hi-Wert viel größer. Daher generiert meine ID während des Tests mit dem häufigen Neustart des Servers die folgenden Sequenz-IDs:
1, 2, 3, 4, 19, 250, 251, 252, 400, 550, 750, 751, 752, 850, 1100, 1150.

Ich weiß, dass Sie bereits gesagt haben, dass dies nicht mit der Spezifikation in Konflikt steht, aber ich glaube, dass dies für die meisten Entwickler eine sehr unerwartete Situation sein wird.

Jeder Beitrag wird sehr hilfreich sein.

Jihwan

UPDATE: ne1410s: Danke für die Bearbeitung.
cfrick: OK. Ich werde das machen. Es war mein erster Beitrag hier und ich war mir nicht sicher, wie ich ihn verwenden sollte.

Jetzt habe ich besser verstanden, warum maxLo für zwei Zwecke verwendet wurde: Da der Ruhezustand die DB-Sequenz einmal aufruft, die ID auf Java-Ebene weiter erhöht und in der DB speichert, sollte der ID-Wert auf Java-Ebene berücksichtigen, wie viel ohne Aufruf geändert wurde die DB-Sequenz beim nächsten Aufruf der Sequenz.

Beispielsweise war die Sequenz-ID zu einem Zeitpunkt 1 und der Ruhezustand wurde mit 5, 6, 7, 8, 9 eingegeben (mit Zuordnungsgröße = 5). Wenn wir das nächste Mal die nächste Sequenznummer erhalten, gibt DB 2 zurück, aber im Ruhezustand müssen 10, 11, 12 verwendet werden. Deshalb ist "hi = lastSourceValue.copy (). MultipllyBy (maxLo + 1)" wird verwendet, um eine nächste ID 10 von den 2 zu erhalten, die von der DB-Sequenz zurückgegeben wurden. Es scheint nur störend zu sein, dass während des häufigen Neustarts des Servers und dies war mein Problem mit der größeren Lücke.

Wenn wir also die SEQUENCE-ID verwenden, stimmt die in die Tabelle eingefügte ID nicht mit der SEQUENCE-Nummer in DB überein.

Jihwan
quelle
1

Nach dem Durchsuchen des Quellcodes im Ruhezustand und der folgenden Konfiguration wird nach 50 Einfügungen der nächste Wert an Oracle db gesendet. Stellen Sie also bei jedem Aufruf ein INST_PK_SEQ-Inkrement von 50 ein.

Der Ruhezustand 5 wird für die folgende Strategie verwendet

Überprüfen Sie auch unten http://docs.jboss.org/hibernate/orm/5.1/userguide/html_single/Hibernate_User_Guide.html#identifiers-generators-sequence

@Id
@Column(name = "ID")
@GenericGenerator(name = "INST_PK_SEQ", 
strategy = "org.hibernate.id.enhanced.SequenceStyleGenerator",
parameters = {
        @org.hibernate.annotations.Parameter(
                name = "optimizer", value = "pooled-lo"),
        @org.hibernate.annotations.Parameter(
                name = "initial_value", value = "1"),
        @org.hibernate.annotations.Parameter(
                name = "increment_size", value = "50"),
        @org.hibernate.annotations.Parameter(
                name = SequenceStyleGenerator.SEQUENCE_PARAM, value = "INST_PK_SEQ"),
    }
)
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "INST_PK_SEQ")
private Long id;
fatih tekin
quelle
3
Tut mir leid, aber dies ist eine äußerst ausführliche Methode, um etwas einzurichten, das sich leicht mit zwei Parametern für einen gesamten Ruhezustand und damit für alle Entitäten ausdrücken lässt.
G. Demecki
wahr, aber wenn ich es mit anderen Methoden versuche, funktioniert keine von ihnen, wenn Sie es haben, kann mir senden, wie Sie konfiguriert haben
fatih tekin
Ich habe meine Antwort aktualisiert - jetzt enthält sie auch ein funktionierendes Beispiel. Obwohl mein Kommentar oben teilweise falsch ist: Leider können Sie weder allocationSizenoch initialValueglobal für alle Entitäten festlegen (es sei denn, Sie verwenden nur einen Generator, aber meiner Meinung nach ist es nicht sehr lesbar).
G. Demecki
1
Vielen Dank für die Erklärung, aber was Sie oben geschrieben haben, habe ich versucht und es hat nicht mit Hibernate 5.0.7 funktioniert. Endgültige Version, dann habe ich mich in den Quellcode vertieft, um dieses Ziel zu erreichen, und das ist die Implementierung, die ich finden konnte im Ruhezustand Quellcode. Die Konfiguration mag schlecht aussehen, aber das ist leider die API für den Ruhezustand und ich verwende die Standard-EntityManager-Implementierung
für den Ruhezustand
1

Auch ich habe dieses Problem in Hibernate 5 gesehen:

@Id
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = SEQUENCE)
@SequenceGenerator(name = SEQUENCE, sequenceName = SEQUENCE)
private Long titId;

Ich habe unten eine Warnung wie diese erhalten:

Gefundene Verwendung des veralteten sequenzbasierten ID-Generators [org.hibernate.id.SequenceHiLoGenerator]; Verwenden Sie stattdessen org.hibernate.id.enhanced.SequenceStyleGenerator. Weitere Informationen finden Sie im Handbuch zur Zuordnung von Domänenmodellen im Ruhezustand.

Dann habe ich meinen Code in SequenceStyleGenerator geändert:

@Id
@GenericGenerator(name="cmrSeq", strategy = "org.hibernate.id.enhanced.SequenceStyleGenerator",
        parameters = {
                @Parameter(name = "sequence_name", value = "SEQUENCE")}
)
@GeneratedValue(generator = "sequence_name")
private Long titId;

Dies löste meine beiden Probleme:

  1. Die veraltete Warnung wurde behoben
  2. Jetzt wird die ID gemäß der Orakelsequenz generiert.
Mohamed Afzal
quelle
0

Ich würde die DDL für die Sequenz im Schema überprüfen. Die JPA-Implementierung ist nur für die Erstellung der Sequenz mit der richtigen Zuordnungsgröße verantwortlich. Wenn die Zuordnungsgröße 50 ist, muss Ihre Sequenz daher das Inkrement 50 in ihrer DDL haben.

Dieser Fall kann normalerweise beim Erstellen einer Sequenz mit der Zuordnungsgröße 1 auftreten, die später auf die Zuordnungsgröße 50 (oder die Standardgröße) konfiguriert wird, aber die Sequenz-DDL wird nicht aktualisiert.

Hasan Ceylan
quelle
Sie verstehen meinen Standpunkt falsch. ALTER SEQUENCE ... INCREMENTY BY 50;wird nichts lösen, weil das Problem immer noch das gleiche bleibt. Der Sequenzwert spiegelt immer noch nicht die IDs der realen Entitäten wider.
G. Demecki
Bitte teilen Sie einen Testfall mit, damit wir das Problem hier besser verstehen können.
Hasan Ceylan
1
Testfall? Warum? Die von mir gestellte Frage war nicht so kompliziert und wurde bereits beantwortet. Es scheint, dass Sie nicht wissen, wie der HiLo-Generator funktioniert. Wie auch immer: Danke, dass Sie Ihre Zeit und Mühe geopfert haben.
G. Demecki
1
Gregory, Eigentlich weiß ich, wovon ich spreche. Ich habe Batoo JPA geschrieben, die% 100 JPA-Implementierung, die sich derzeit in der Inkubation befindet und Hibernate in Bezug auf die Geschwindigkeit übertrifft - 15-mal schneller. Andererseits habe ich Ihre Frage möglicherweise falsch verstanden und nicht gedacht, dass die Verwendung von Hibernate mit Sequenzen überhaupt ein Problem darstellen sollte, da ich Hibernate seit 2003 in vielen Projekten in vielen Datenbanken verwendet habe. Das Wichtigste ist, dass Sie eine Lösung für die Frage haben. Tut mir leid, dass ich die als richtig markierte Antwort verpasst habe ...
Hasan Ceylan
Entschuldigung, ich wollte dich nicht beleidigen. Nochmals vielen Dank für Ihre Hilfe, Frage wird beantwortet.
G. Demecki