# JPA OptimisticLocking Problem



## sebastian_goetz (10. Aug 2011)

Hallo zusammen,

Ich habe derzeit ein Problem mit dem Optimistic Locking von JPA (Hibernate als EntityManager). Meine Tabelle hat eine @Version-Spalte anhand derer sich Hibernate ermittelt, ob eine konkurrierende Änderung stattfand. So weit so gut. Auf diesen Daten gibt es auch keinen Hintergrundprozess, der eventuell eine Änderung machen könnte (nur um dies vorab auszuschließen). Es existiert derzeit kein konkurrierender Zugriff, da ich der einzige Tester bin.
Nun ist es aber so, dass wenn ich einen Datensatz ändere und speichere, diese @Version-Spalte automatisch vom EntityManager aktualisiert wird. Nach der Änderung habe ich dann (nicht immer aber oft) eine minimale Differenz zwischen dem Wert der @Version-Spalte im RAM und in der Datenbank. Das sind nur 1 oder 2 Millisekunden, aber das reicht natürlich um beim nächsten Speichern eine OptimisticLockException auszulösen. Daher vermute ich hier einen Fehler.

Kann mir jemand dazu etwas sagen?

Vielen Dank für jeden Hinweis.

Sebastian


----------



## turtle (10. Aug 2011)

> Nach der Änderung habe ich dann eine Differenz zwischen dem Wert der @Version-Spalte im RAM und in der Datenbank



Ist es nicht so, dass genau so optimistisches Locking funktioniert?


----------



## sebastian_goetz (10. Aug 2011)

Hallo turtle,

Wahrscheinlich habe ich mich mal wieder nicht klar ausgedrückt. *grins*
Es ist so das der Datensatz der vom EntityManager nach dem update zurückgegeben wird nicht den exakt gleichen Timestamp hat wie der entsprechende Datensatz in der Datenbank.

Vielleicht mit einem Beispiel:

Ich hab eine Tabelle Kunde (Name, Datum). Dazu passend gibt es ein Kunde-Entity welche auf der property 'Datum' die @Version-Annotation hat.
1. Ich lese einen Kunden aus der Datenbank. Dieser habe nun das Datum (resp. Version) 10.08.2011 12:00:00.000
2. Über die Oberfläche ändere ich nun den Namen und speichere den Kunden.
3. Der EntityManager merged meine Änderung in die Datenbank und erzeugt eine neue Version, in dem er die property 'Datum' erhöht.
4. Wenn ich jetzt die Werte in der Datenbank und im Kunde-Objekt vergleiche, habe ich fast immer Unterschiede im Bereich weniger Millisekunden (z.B. 10.08.2011 12:01:29.123 zu 10.08.2011 12:01:29.124)

Das Update funktioniert ohne Probleme. Arbeite ich nun allerdings mit diesem Kunde-Objekt weiter, ändere wiederum den Namen und speichere erneut, so kommt es aufgrund dieser minimalen Diskrepanz in der Version zu einer OptimisticLockException aufgrund von 'stale data'.

Gruß

Sebastian


----------



## AFlieger (10. Aug 2011)

Wenn du anstatt einer Datumsspalte eine einfache Integer-Spalte mit @Version annotierst, solltest du dem Problem aus dem Weg gehen können.


----------



## sebastian_goetz (10. Aug 2011)

Danke das stimmt schon, löst aber nicht das Problem.
Gibt es denn eine Möglichkeit eine mehr oder weniger automatisierte Aktualisierung eines Timestamps vorzunehmen OHNE dass es sich bei der property um eine @Version handelt?


----------



## turtle (10. Aug 2011)

EntityListener


----------



## AFlieger (10. Aug 2011)

Du kannst in einer Entity eine Methode mit @PrePersist annotieren und dort das Datum setzen.

Ich denke, dass dir dieser Link weiterhelfen wird; ich denke, dass sich dies auf alle DB'en übertragen lässt.

Creation Zeitstempel und last update Zeitstempel mit Hibernate und MySQL


----------



## sebastian_goetz (10. Aug 2011)

Danke ihr beiden.

Der Hinweis mit dem EntityListener hat mich auf die richtige Spur gebracht.

Hat jemand von euch schon einmal etwas vergleichbares gemacht allerdings um sich den ändernden User zu merken? Das Problem ist ja, dass das in der JPA-Schicht nicht mehr bekannt ist, welcher User die Änderung verursacht.

Gruß

Sebastian


----------



## AFlieger (10. Aug 2011)

Das sollte eigentlich analog dem Datumsfeld funktionieren.

Du wirst sicherlich irgendwo in deiner Applikation die Information über den aktuell angemeldeten Benutzer haben.

Diese Informaion dann einfach in das entsprechende Property der Entit setzen und dann sollte es doch gehen.


----------



## sebastian_goetz (10. Aug 2011)

AFlieger hat gesagt.:


> Du wirst sicherlich irgendwo in deiner Applikation die Information über den aktuell angemeldeten Benutzer haben.



Irgendwo schon. Aber nicht in der persistence-layer. Aber der Ansatz mit dem EntityListener gefällt mir gut. Wenn ich dafür eine brauchbare Lösung finde, poste ich sie hier.

Sebastian


----------



## AFlieger (10. Aug 2011)

> Irgendwo schon. Aber nicht in der persistence-layer.



Gut, aber dann kannst du die Information ja schon in der Business-Schicht setzen, eben dort, wo die anderen Daten in der Entity gelangen.


----------



## sebastian_goetz (10. Aug 2011)

Es soll ja aber möglichst an einer zentralen Stelle sein, so dass ich nicht in jedem bean beim Abspeichern dran denken muss, den angemeldeten Benutzer zu setzen...


----------

