Angenommen, Sie haben eine Art Datenstruktur, die in einer Art Datenbank beibehalten wird. Nennen wir diese Datenstruktur der Einfachheit halber Person
. Sie müssen nun eine CRUD-API entwerfen, mit der andere Anwendungen Person
s erstellen, lesen, aktualisieren und löschen können . Nehmen wir zur Vereinfachung an, dass auf diese API über eine Art Webdienst zugegriffen wird.
Für die Teile C, R und D von CRUD ist das Design einfach. Ich werde eine C # -ähnliche Funktionsnotation verwenden - die Implementierung könnte SOAP, REST / JSON oder etwas anderes sein:
class Person {
string Name;
DateTime? DateOfBirth;
...
}
Identifier CreatePerson(Person);
Person GetPerson(Identifier);
void DeletePerson(Identifier);
Was ist mit Update? Das Natürlichste wäre
void UpdatePerson(Identifier, Person);
aber wie würden Sie angeben, welche Felder Person
aktualisiert werden sollen?
Lösungen, die ich finden könnte:
Sie können immer verlangen, dass eine vollständige Person übergeben wird, dh der Kunde würde so etwas tun, um das Geburtsdatum zu aktualisieren:
p = GetPerson(id); p.DateOfBirth = ...; UpdatePerson(id, p);
Dies würde jedoch eine Art Transaktionskonsistenz oder eine Sperrung zwischen Get und Update erfordern. Andernfalls können Sie eine andere Änderung überschreiben, die parallel von einem anderen Client vorgenommen wurde. Dies würde die API viel komplizierter machen. Darüber hinaus ist es fehleranfällig, da der folgende Pseudocode (unter der Annahme einer Client-Sprache mit JSON-Unterstützung)
UpdatePerson(id, { "DateOfBirth": "2015-01-01" });
- was richtig aussieht - würde nicht nur DateOfBirth ändern, sondern auch alle anderen Felder auf null zurücksetzen.
Sie können alle Felder ignorieren
null
. Wie würden Sie dann einen Unterschied machen, ob Sie es nicht ändernDateOfBirth
oder absichtlich auf null ändern ?Ändern Sie die Signatur in
void UpdatePerson(Identifier, Person, ListOfFieldNamesToUpdate)
.Ändern Sie die Signatur in
void UpdatePerson(Identifier, ListOfFieldValuePairs)
.Verwenden Sie eine Funktion des Übertragungsprotokolls: Sie können beispielsweise alle Felder ignorieren, die nicht in der JSON-Darstellung der Person enthalten sind. Dies erfordert jedoch normalerweise, dass Sie den JSON selbst analysieren und die integrierten Funktionen Ihrer Bibliothek (z. B. WCF) nicht verwenden können.
Keine der Lösungen erscheint mir wirklich elegant. Dies ist sicherlich ein häufiges Problem. Was ist also die Best-Practice-Lösung, die von allen verwendet wird?
quelle
Person
Lassen Sie für neu erstellte Instanzen, die noch nicht persistiert sind, und für den Fall, dass der Bezeichner als Teil des Persistenzmechanismus festgelegt wird, ihn einfach auf null. Für die Antwort verwendet JPA eine Versionsnummer. Wenn Sie Version 23 lesen und das Element aktualisieren, wenn die Version in DB 24 ist, schlägt der Schreibvorgang fehl.PUT
als auchPATCH
Methoden. Bei VerwendungPATCH
sollten nur Sendeschlüssel ersetzt werden, wobeiPUT
das gesamte Objekt ersetzt wird.Antworten:
Wenn Sie keine Nachverfolgung von Änderungen als Anforderung für dieses Objekt haben (z. B. "Benutzer John hat Name und Geburtsdatum geändert"), ist es am einfachsten, das gesamte Objekt in der Datenbank mit einem Objekt zu überschreiben, das Sie vom Verbraucher erhalten. Dieser Ansatz würde etwas mehr Daten beinhalten, die über Kabel gesendet werden, aber Sie vermeiden das Lesen vor dem Update.
Wenn Sie eine Aktivitätsverfolgungsanforderung haben. Ihre Welt ist viel komplizierter und Sie müssen festlegen, wie Informationen über CRUD-Aktionen gespeichert und abgefangen werden sollen. Das ist die Welt, in die du nicht eintauchen willst, wenn du keine solchen Anforderungen hast.
In Bezug auf das Überschreiben von Werten durch separate Transaktionen würde ich vorschlagen, nach optimistischen und pessimistischen Sperren zu suchen . Sie mildern dieses häufige Szenario:
Jeder Benutzer hat eine andere Transaktion, daher Standard-SQL. Am häufigsten ist optimistisches Sperren (auch von @ SJuan76 im Kommentar zu Versionen erwähnt). Ihre Version Ihr Datensatz in der Datenbank und während des Schreibens werfen Sie zuerst einen Blick in die Datenbank, wenn die Versionen übereinstimmen. Wenn die Versionen nicht übereinstimmen, wissen Sie, dass in der Zwischenzeit jemand das Objekt aktualisiert hat, und Sie müssen dem Verbraucher eine Fehlermeldung zu dieser Situation senden. Ja, Sie müssen diese Situation dem Benutzer zeigen.
Beachten Sie, dass Sie den tatsächlichen Datensatz vor dem Schreiben aus der Datenbank lesen müssen (für einen optimistischen Vergleich der Sperrversionen), sodass für die Implementierung der Delta-Logik (nur geänderte Werte schreiben) möglicherweise keine zusätzliche Leseabfrage vor dem Schreiben erforderlich ist.
Die Einführung der Delta-Logik hängt in hohem Maße vom Vertrag mit dem Verbraucher ab. Beachten Sie jedoch, dass es für den Verbraucher am einfachsten ist, die volle Nutzlast anstelle von Delta zu erstellen.
quelle
Wir haben eine PHP-API in Arbeit. Wenn für Aktualisierungen kein Feld im JSON-Objekt gesendet wird, wird es auf NULL gesetzt. Dann wird alles an die gespeicherte Prozedur übergeben. Die gespeicherte Prozedur versucht, jedes Feld mit field = IFNULL (Eingabe, Feld) zu aktualisieren. Wenn sich also nur 1 Feld im JSON-Objekt befindet, wird nur dieses Feld aktualisiert. Um ein festgelegtes Feld explizit zu leeren, muss field = '' vorhanden sein. Die Datenbank aktualisiert das Feld dann entweder mit einer leeren Zeichenfolge oder mit dem Standardwert dieser Spalte.
quelle
Geben Sie die Liste der aktualisierten Felder in der Abfragezeichenfolge an.
PUT /resource/:id?fields=name,address,dob Body { //resource body }
Implementieren Sie das Zusammenführen gespeicherter Daten mit dem Modell aus dem Anforderungshauptteil:
quelle