Grundsätzlich versuche ich zu verstehen, wie man den Transaktionscode richtig schreibt (oder "richtig schreibt"), wenn man den REST-Dienst mit Jax-RS und Spring entwickelt. Außerdem verwenden wir JOOQ für den Datenzugriff. Aber das sollte nicht sehr relevant sein.
Betrachten wir das einfache Modell, in dem wir einige Organisationen haben, die diese Felder haben: "id", "name", "code"
. Alle müssen einzigartig sein. Außerdem gibt es ein status
-Feld.
Die Organisation könnte irgendwann entfernt werden. Aber wir wollen die Daten nicht komplett entfernen, weil wir sie für Analyse- / Wartungszwecke speichern wollen. Also setzen wir das Organisationsstatusfeld auf 'REMOVED'
.
Da wir die Organisationszeile nicht aus der Tabelle löschen, können wir die eindeutige Einschränkung nicht einfach auf die Spalte "Name" anwenden, da wir möglicherweise die Organisation löschen und dann eine neue mit dem gleichen Namen erstellen. Aber nehmen wir an, dass Codes global eindeutig sein müssen, also haben wir eine einzigartige Einschränkung für die Spalte code
.
Sehen wir uns also dieses einfache Beispiel an, das eine Organisation erstellt und einige Prüfungen auf dem Weg durchführt.
Ressource:
%Vor%DAO-Dienst sieht so aus:
%Vor%Das bringt einige Fragen:
@Transactional
Annotation in die Methode createOrganization
der Ressource einfügen? Oder sollte ich einen weiteren Dienst erstellen, der mit DAO kommuniziert und @Transactional Annotation zu seiner Methode hinzufügt? Etwas anderes? "code"
-Feld senden? Bevor die erste Transaktion ausgeführt wird, werden die Überprüfungen erfolgreich durchgeführt, sodass keine 409 Rückmeldungen gesendet werden. Als erste Transaktion wird ordnungsgemäß übergeben, aber die zweite Transaktion wird die DB-Einschränkung verletzen. Dies wird SQLException auslösen. Wie man das anmutig handhabt? Ich meine, ich möchte immer noch eine nette Fehlermeldung auf der Client-Seite zeigen, dass der Name bereits benutzt wird. Aber ich kann SQLException oder etw nicht wirklich parsen. Kann ich? UPDATE: Ich kenne die Isolationsstufe und die übliche Fehler / Isolationsmatrix (Dirty Reads, etc ..). ). Das Problem, das ich habe, ist, einige "produktionsreife" Beispiele zu finden, von denen ich lernen kann. Oder ein gutes Buch zu einem Thema. Ich bekomme immer noch nicht wirklich, wie man alle Fehler richtig behandelt .. Ich denke, ich muss ein paar Mal wiederholen, wenn die Transaktion fehlgeschlagen .. und dann nur einige generische Fehler und implementieren Client, der das handhabt .. Aber tun Ich muss wirklich den SERIALIZABLE-Modus verwenden, wenn ich Bereichsabfragen verwende? Weil es die Leistung stark beeinträchtigt. Aber sonst wie kann ich garantieren, dass die Transaktion fehlschlägt ..
Wie auch immer, ich habe beschlossen, dass ich im Moment mehr Zeit brauche, um etwas über Transaktionen und das DB-Management zu lernen, um dieses Problem anzugehen ...
Im Allgemeinen sollte der Endpunkt, ohne über Transaktionalität zu sprechen, nur Parameter aus der Anfrage abrufen und den Service anrufen. Es sollte keine Geschäftslogik machen.
Es scheint, dass Ihre checkXXX-Methoden Teil der Geschäftslogik sind, weil sie Fehler bei domänenspezifischen Konflikten auslösen. Warum sollte man sie nicht in eine einzige Methode einfügen, die übrigens transaktional ist?
%Vor%Ich habe genommen, da Ihre Parameter Strings sind, aber sie können alles sein. Ich bin mir nicht sicher, ob Sie Responses.abortConflict in der Service-Schicht werfen wollen, weil es sich um ein REST-Konzept handelt, aber Sie können bei Bedarf eigene Ausnahme-Typen definieren.
Der Endpunktcode sollte so aussehen, er könnte jedoch einen zusätzlichen try-catch-Block enthalten, der die geworfenen Ausnahmen in Fehlerreaktionen umwandelt:
%Vor%Wie bei Frage 2 und 3 sind Transaktionsisolationsstufen Ihre Freunde. Stellen Sie die Isolationsstufe hoch genug ein. Ich denke, wiederholbares Lesen ist in Ihrem Fall das Richtige. Ihre checkXXX-Methoden erkennen, ob eine andere Transaktion Entitäten mit demselben Namen oder Code festschreibt, und garantiert, dass die Situationen bis zur Ausführung der create-Methode bestehen bleiben. Ein weiteres nützliches Lesen bezüglich Spring- und Transaktionsisolationslevels.
Nach meinem Verständnis der beste Weg, Transaktion auf DB-Ebene zu behandeln, müssen Sie Spring Isolation Trnsaktion in effektiver Weise in der Dao-Schicht verwenden. Unten ist beispiel industrie standard codde in ihrem fall ...
Bitte zwickt mich, wenn ich hier falsch liege
Trennung von Bedenken:
RuntimeException
). Ich bestehe auf eine Sache, der gute Ort, um Transaktionsgrenzen zu haben, ist der Ort, an dem Ihre Geschäftsmethoden definiert sind. Ein Transaktionsbereich muss eine Geschäftseinheit der Arbeit sein.
Was das Problem der Parallelität betrifft, gibt es zwei Möglichkeiten, um diese Art von Nebenläufigkeitsproblemen zu lösen: pessimistisches oder optimistisches Sperren.
Pessimistisch:
Optimistisch:
Pessimistisch ist ein Problem in Bezug auf Skalierbarkeit und Leistung. Das optimistische Problem besteht darin, dass Sie manchmal einen Bedienungsfehler an den Endbenutzer senden.
Ich würde persönlich mit einer optimistischen Sperrung in Ihrem Fall gehen, JOOQ unterstützt es
Zunächst einmal sollte die DAO-Ebene nicht einmal wissen, dass sie von einem REST-Webservice angeboten wird. Stellen Sie sicher, dass Sie die Verantwortlichkeiten voneinander trennen.
Halten Sie die @Transactional auf dem DAO. Wenn Sie nur eine einzige Anweisung ausgeben, müssen Sie entscheiden, ob Sie mit Dirty Reads einverstanden sind. Finde heraus, was die niedrigste Isolationsstufe für deine Anwendung ist. Jede Methode startet eine neue Transaction (sofern nicht von einer anderen Methode aufgerufen, die bereits gestartet wurde) und wenn Exceptions ausgelöst werden, werden alle Aufrufe zurückgesetzt. Sie können einen benutzerdefinierten ExceptionHandler in Ihrem Controller einrichten, um SQLDataIntegrityExceptions zu verarbeiten (wie bei Ihrem Beispielcode).
Verwenden Sie einen Gesamtprimärschlüssel, der (ID, Name, Code, Status) abdeckt, so dass Sie eine Organisation mit demselben Namen haben können, aber einer ist "AKTUELL" und einer wird "ENTFERNT"
seinTags und Links java spring jax-rs rest transactions