Sollte Hibernate Session # merge eine Insert beim Empfangen einer Entity mit einer ID?

9

Es scheint, als würde es oft kommen, aber ich habe ohne Erfolg gegoogelt.

Angenommen, Sie haben eine Hibernate-Entität User . Sie haben eine User in Ihrem DB mit der ID 1.

Sie haben zwei laufende Threads, A und B. Sie tun Folgendes:

  • A erhält Benutzer 1 und close s ist Session
  • B erhält Benutzer 1 und delete s it
  • A ändert ein Feld auf Benutzer 1
  • A erhält ein neues Session und merge s Benutzer 1

All meine Tests zeigen an, dass merge versucht, Benutzer 1 in der DB zu finden (das kann natürlich nicht), also fügt er einen neuen Benutzer mit der ID 2 ein.

Meine Erwartung wäre andererseits, dass Hibernate sehen würde, dass der Benutzer, der zusammengeführt wird, nicht neu ist (weil er eine ID hat). Es würde versuchen, den Benutzer in der DB zu finden, was fehlschlagen würde, also würde es keine Einfügung oder ein Update versuchen. Idealerweise würde es irgendeine Art von Gleichzeitigkeitsausnahme auslösen.

Beachten Sie, dass ich optimistisches Sperren über @Version verwende, und das hilft nicht weiter.

Also, Fragen:

  1. Ist mein beobachtetes Hibernate-Verhalten das beabsichtigte Verhalten?
  2. Wenn ja, ist es das gleiche Verhalten beim Aufruf von merge in einem JPA EntityManager anstelle eines Hibernate Session ?
  3. Wenn die Antwort auf 2 ja lautet, warum beschwert sich niemand darüber?
David van Geest 31.01.2014, 20:19
quelle

3 Antworten

0

Ich habe JSR-220 betrachtet, von dem Session#merge beansprucht, seine Semantik zu erhalten . Der JSR ist leider mehrdeutig, habe ich gefunden.

Es heißt:

  

Optimistisches Sperren ist eine Technik, die verwendet wird, um diese Aktualisierungen sicherzustellen   Zu der Datenbank werden Daten, die dem Zustand einer Entität entsprechen, gemacht   nur wenn keine intervenierende Transaktion diese Daten seit der Aktualisierung aktualisiert hat   Entitätsstatus wurde gelesen.

Wenn Sie "Aktualisierungen" nehmen, um allgemeine Mutationen der Datenbankdaten, einschließlich Löschungen, und nicht nur ein SQL UPDATE , was ich tue, zu beachten, können Sie argumentieren, dass das beobachtete Verhalten nicht mit optimistisch übereinstimmt Sperren.

Viele Leute stimmen zu, angesichts der Kommentare zu meiner Frage und der darauffolgenden Entdeckung von diesem Fehler .

>

Aus rein praktischer Sicht könnte das Verhalten, ob konform oder nicht, zu einigen Fehlern führen, weil es den Erwartungen vieler Entwickler widerspricht. Es scheint keine einfache Lösung dafür zu geben. In der Tat scheint Spring Data JPA dieses Problem vollständig zu ignorieren, indem man blindlings using EM#merge . Möglicherweise gehen andere JPA-Anbieter anders damit um, aber mit Hibernate könnte dies zu Problemen führen.

Ich arbeite derzeit daran, indem ich derzeit Session#update verwende. Es ist wirklich hässlich und erfordert Code, um den Fall zu behandeln, wenn Sie versuchen, update eine Entität losgelöst zu machen, und es gibt bereits eine verwaltete Kopie davon. Aber es wird auch nicht zu falschen Inserts führen.

    
David van Geest 03.02.2014, 19:49
quelle
1

Bitte lesen Sie den Text aus der Winterschlaf-Dokumentation.

Kopieren Sie den Status des angegebenen Objekts auf das persistente Objekt mit demselben Bezeichner. Wenn der Sitzung derzeit keine persistente Instanz zugeordnet ist, wird sie geladen. Gibt die persistente Instanz zurück. Wenn die angegebene Instanz nicht gespeichert ist, speichern Sie eine Kopie von und geben Sie sie als neu persistente Instanz zurück.

Es wurde klar gesagt, dass der Zustand (Daten) des Objekts in der Datenbank kopiert werden soll. Wenn das Objekt nicht vorhanden ist, speichern Sie eine Kopie dieser Daten. Wenn wir sagen, speichern Sie eine Kopie Hibernate immer einen Datensatz mit neuer Kennung erstellen.

Die Hibernate-Merge-Funktion funktioniert etwa folgendermaßen:

  1. Er prüft den Status (angehängt oder entfernt von der Sitzung) der Entität und fand ihn getrennt.
  2. Dann versucht es, die Entität mit dem Bezeichner zu laden, aber nicht in der Datenbank gefunden.
  3. Da die Entität nicht gefunden wird, behandelt sie diese Entität als transient.
  4. Transiente Entität erstellt immer einen neuen Datenbankeintrag mit neuer Kennung.

Die Sperre wird immer auf verbundene Entitäten angewendet. Wenn die Entity getrennt ist, wird sie immer im Ruhezustand geladen und der Versionswert wird aktualisiert.

Sperren wird verwendet, um Nebenläufigkeitsprobleme zu kontrollieren. Es ist nicht das Problem der Parallelität.

    
Sumit Tyagi 02.02.2014 06:26
quelle
0

1. Ist mein beobachtetes Hibernate-Verhalten das beabsichtigte Verhalten?

Das Verhalten ist korrekt. Sie versuchen nur Operationen auszuführen, die nicht gegen gleichzeitige Datenänderungen geschützt sind. Wenn Sie die Operation in zwei Sitzungen aufteilen müssen. Suchen Sie einfach das Objekt für die Aktualisierung erneut und prüfen Sie, ob es noch da ist, werfen Sie die Ausnahme, wenn nicht. Wenn es einen gibt, sperren Sie es mit em. (Klasse, Primärschlüssel, LockModeType); oder verwenden Sie @Version oder @Entity (optimisticLock = OptimisticLockType.ALL / DIRTY / VERSION), um das Objekt bis zum Ende der Transaktion zu schützen.

2.Wie lautet das Verhalten beim Aufruf von Merge auf einem JPA-EntityManager anstelle einer Hibernate-Sitzung?

Wahrscheinlich: ja

3.Wenn die Antwort auf 2. ja ist, warum beschwert sich niemand darüber?

Wenn Sie Ihre Operationen mit pessimistischen oder optimistischen Sperren schützen, verschwindet das Problem:)

Das Problem, das Sie lösen möchten, heißt: Nicht wiederholbares Lesen

    
Marek Raszewski 01.02.2014 00:41
quelle

Tags und Links