Załóżmy, że masz jakąś strukturę danych, która jest utrwalona w jakiejś bazie danych. Dla uproszczenia nazwijmy tę strukturę danych Person
. Masz teraz zadanie zaprojektowania interfejsu CRUD API, który pozwala innym aplikacjom tworzyć, czytać, aktualizować i usuwać Person
s. Dla uproszczenia załóżmy, że dostęp do tego interfejsu API można uzyskać za pośrednictwem usługi internetowej.
W przypadku części CRUD C, R i D konstrukcja jest prosta. Użyję notacji funkcjonalnej podobnej do C # - implementacją może być SOAP, REST / JSON lub coś innego:
class Person {
string Name;
DateTime? DateOfBirth;
...
}
Identifier CreatePerson(Person);
Person GetPerson(Identifier);
void DeletePerson(Identifier);
Co z aktualizacją? Naturalną rzeczą do zrobienia byłoby
void UpdatePerson(Identifier, Person);
ale jak określisz, które pola Person
chcesz zaktualizować?
Rozwiązania, które mógłbym wymyślić:
Zawsze możesz wymagać przekazania pełnej Osoby, tzn. Klient zrobiłby coś takiego, aby zaktualizować datę urodzenia:
p = GetPerson(id); p.DateOfBirth = ...; UpdatePerson(id, p);
Wymagałoby to jednak pewnego rodzaju spójności transakcyjnej lub blokowania między Get a Update; w przeciwnym razie możesz zastąpić inną zmianę wykonaną równolegle przez innego klienta. Sprawiłoby to, że interfejs API byłby znacznie bardziej skomplikowany. Ponadto jest podatny na błędy, ponieważ następujący pseudo-kod (zakładając język klienta z obsługą JSON)
UpdatePerson(id, { "DateOfBirth": "2015-01-01" });
- który wygląda poprawnie - nie tylko zmieni DateOfBirth, ale także zresetuje wszystkie inne pola do null.
Możesz zignorować wszystkie pola, które są
null
. Jak jednak zrobiłbyś różnicę między niezmienianiemDateOfBirth
a celowym zmienianiem go na zero ?Zmień podpis na
void UpdatePerson(Identifier, Person, ListOfFieldNamesToUpdate)
.Zmień podpis na
void UpdatePerson(Identifier, ListOfFieldValuePairs)
.Użyj pewnej funkcji protokołu transmisji: na przykład możesz zignorować wszystkie pola nie zawarte w reprezentacji JSON osoby. Zwykle wymaga to jednak samodzielnego przeanalizowania JSON i niemożności korzystania z wbudowanych funkcji biblioteki (np. WCF).
Żadne z rozwiązań nie wydaje mi się naprawdę eleganckie. Z pewnością jest to powszechny problem, więc jakie jest najlepsze rozwiązanie stosowane przez wszystkich?
źródło
Person
instancji, które nadal nie są utrwalane, a jeśli identyfikator jest ustalany jako część mechanizmu trwałości, pozostaw go zerowy. Jeśli chodzi o odpowiedź, JPA używa numeru wersji; jeśli czytasz wersję 23, po zaktualizowaniu elementu, jeśli wersja w DB to 24, zapis nie powiedzie się.PUT
iPATCH
metody. Podczas korzystaniaPATCH
należy wymieniać tylko klucze wysyłania, zastępującPUT
cały obiekt.Odpowiedzi:
Jeśli nie masz śledzenia zmian jako wymaganego dla tego obiektu (np. „Użytkownik Jan zmienił imię i datę urodzenia”), najprostszym byłoby zastąpienie całego obiektu w DB tym, który otrzymałeś od konsumenta. Takie podejście wymagałoby nieco większej ilości danych przesyłanych przewodowo, ale unika się odczytu przed aktualizacją.
Jeśli masz wymóg śledzenia aktywności. Twój świat jest znacznie bardziej skomplikowany i musisz zaprojektować, jak przechowywać informacje o działaniach CRUD i jak je przechwytywać. To świat, w który nie chcesz nurkować, jeśli nie masz takich wymagań.
Jeśli chodzi o nadpisywanie wartości oddzielnymi transakcjami, sugerowałbym przeprowadzenie badań dotyczących optymistycznego i pesymistycznego blokowania . Łagodzą ten typowy scenariusz:
Każdy użytkownik ma inną transakcję, dlatego też standardowy SQL. Najczęstszym jest optymistyczne blokowanie (wspomniane również przez @ SJuan76 w komentarzu na temat wersji). Twoja wersja jest Twoim rekordem w DB, a podczas pisania najpierw sprawdzasz DB, jeśli wersje się zgadzają. Jeśli wersje się nie zgadzają, wiesz, że w międzyczasie ktoś zaktualizował obiekt i musisz odpowiedzieć komunikatem o błędzie dla konsumenta na temat tej sytuacji. Tak, musisz pokazać tę sytuację użytkownikowi.
Zauważ, że musisz odczytać aktualny rekord z DB przed jego zapisaniem (dla optymistycznego porównania wersji blokowania), więc implementacja logiki delta (tylko zmienione wartości zapisu) może nie wymagać dodatkowego zapytania odczytu przed zapisem.
Wprowadzenie logiki delta w dużym stopniu zależy od umowy z konsumentem, ale zauważ, że najłatwiej dla konsumenta jest zbudowanie pełnego ładunku zamiast delty.
źródło
Mamy działający interfejs API PHP. W przypadku aktualizacji, jeśli pole nie jest wysyłane w obiekcie JSON, zostaje ustawione na NULL. Następnie przekazuje wszystko do procedury składowanej. Procedura składowana próbuje zaktualizować każde pole za pomocą pola = IFNULL (dane wejściowe, pole). Więc jeśli tylko 1 pole znajduje się w obiekcie JSON, tylko to pole jest aktualizowane. Aby jawnie opróżnić ustawione pole, musimy mieć pole = '', DB następnie aktualizuje pole pustym łańcuchem lub wartością domyślną tej kolumny.
źródło
Podaj zaktualizowaną listę pól w ciągu zapytania.
PUT /resource/:id?fields=name,address,dob Body { //resource body }
Zaimplementuj scalanie przechowywanych danych z modelem z treści żądania:
źródło