Optimistisches Sperren, das beim manuellen Einstellen des Versionsfeldes keine Ausnahme auslöst

9

Ich habe eine Spring Boot 1.3.M1-Webanwendung mit Spring Data JPA. Für optimistisches Sperren mache ich folgendes:

  1. Beschriften Sie die Versionsspalte in der Entität: @Version private long version; . Ich habe anhand der Datenbanktabelle bestätigt, dass dieses Feld richtig inkrementiert wird.
  2. Wenn ein Benutzer eine Entität zur Bearbeitung anfordert, sendet er auch das Feld version .
  3. Wenn der Benutzer nach der Bearbeitung submit drückt, erhält er das version -Feld als verstecktes Feld oder etwas.
  4. Serverseitig, Abrufen einer neuen Kopie der Entität und anschließendes Aktualisieren der gewünschten Felder zusammen mit dem Feld version . So:

    %Vor%

Ich habe erwartet, dass dies eine Ausnahme auslöst, wenn die Versionen nicht übereinstimmen. Aber das tut es nicht. Beim googeln habe ich einige Posts gefunden, die besagen, dass wir die @Vesion -Eigenschaft einer angehängten Entität nicht wie in der dritten obigen Anweisung festlegen können.

Ich vermute also, dass ich manuell nach der Versionsabweichung suchen muss und die Ausnahme selbst auslösen muss. Wäre das der richtige Weg, oder fehlt mir etwas?

    
Sanjay 17.06.2015, 01:52
quelle

3 Antworten

12

Leider (zumindest für Hibernate) wird das Ändern des @Version -Feldes nicht zu einer anderen "Version" führen. d. h., die Prüfung der optimistischen Gleichzeitigkeit erfolgt für den Versionswert, der beim Lesen der Entität abgerufen wird, nicht für das Versionsfeld der Entität, wenn diese aktualisiert wird.

z.B.

Das wird funktionieren

%Vor%

Dies wird jedoch nicht funktionieren

%Vor%

Es gibt einige Möglichkeiten, dies zu umgehen. Der einfachste Weg besteht wahrscheinlich darin, eine optimistische Gleichzeitigkeitsprüfung selbst durchzuführen. Früher hatte ich ein Utility, um die Datenpopulation "DTO to Model" zu erstellen, und ich habe diese Versionsüberprüfungslogik dort abgelegt. Eine andere Möglichkeit besteht darin, die Logik in setVersion() zu setzen, anstatt die Version tatsächlich zu setzen, die Versionsüberprüfung durchzuführen:

%Vor%     
Adrian Shum 17.06.2015, 02:15
quelle
0

Ein Teil der Antwort @AdrianShum ist korrekt.

Das versionsvergleichende Verhalten folgt grundsätzlich diesen Schritten:

  1. Abrufen der versionierten Entität mit ihrer Versionsnummer, mit der Bezeichnung V1.
  2. Angenommen, Sie ändern die Eigenschaft einer Entität, dann erhöht Hibernate die Versionsnummer auf V2 "im Speicher". Es berührt nicht die Datenbank.
  3. Sie übernehmen die Änderungen, oder sie werden automatisch von der Umgebung übernommen. Hibernate versucht dann, die Entität einschließlich ihrer Versionsnummer mit dem Wert V2 zu aktualisieren. Die von Hibernate generierte Aktualisierungsabfrage ändert nur dann die Registrierung der Entität, wenn sie mit der ID und der vorherigen Versionsnummer (V1) übereinstimmt.
  4. Nachdem die Entitätsregistrierung erfolgreich geändert wurde, nimmt die Entität V2 als ihren tatsächlichen Versionswert an.

Nehmen Sie nun an, dass die Entität zwischen den Schritten 1 und 3 durch eine andere Transaktion geändert wurde, so dass ihre Versionsnummer in Schritt 3 nicht V1 ist. Dann, da die Versionsnummer unterschiedlich ist, wird die Update-Abfrage keine Registrierung ändern, Hibernate das realisieren und die Ausnahme auslösen.

Sie können dieses Verhalten einfach testen und prüfen, ob die Ausnahme ausgelöst wird und die Versionsnummer zwischen Schritt 1 und 3 direkt in Ihrer Datenbank geändert wird.

Bearbeiten. Ich weiß nicht, welchen JPA-Persistenzanbieter Sie mit Spring Data JPA verwenden, aber für weitere Details zum optimistischen Sperren mit JPA + Hibernate empfehle ich Ihnen, Kapitel 10, Abschnitt Kontrolle des gleichzeitigen Zugriffs , des Buches Java-Persistenz mit Hibernate (Hibernate in Aktion)

    
Guillermo 17.06.2015 15:50
quelle
0

Neben der Antwort von @Adrian Shum möchte ich zeigen, wie ich dieses Problem gelöst habe. Wenn Sie eine Version von Entity manuell ändern und ein Update durchführen möchten, um OptimisticConcurrencyException zu verursachen, können Sie einfach Entity mit all seinem Feld kopieren, was dazu führt, dass eine Entity ihren Kontext verlässt (wie EntityManager.detach() ). Auf diese Weise verhält es sich richtig.

%Vor%

BEARBEITEN: Die assemblierte Version funktioniert nur, wenn der Ruhezustand-Cache keine Entität mit derselben ID enthält. Dies wird nicht funktionieren:

%Vor%     
Dmitry 07.02.2017 17:02
quelle

Tags und Links