CRUD API : 업데이트 할 필드를 어떻게 지정합니까?


9

어떤 종류의 데이터베이스에 유지되는 일종의 데이터 구조가 있다고 가정 해 봅시다. 간단히하기 위해이 데이터 구조를 호출 해 봅시다 Person. 이제 CRUD API를 설계해야하는데,이를 통해 다른 응용 프로그램에서을 만들고 읽고 업데이트하고 삭제할 수 Person있습니다. 간단하게하기 위해이 API는 어떤 종류의 웹 서비스를 통해 액세스한다고 가정합니다.

CRUD의 C, R 및 D 부분의 경우 설계가 간단합니다. C #과 유사한 기능 표기법을 사용하겠습니다. 구현은 SOAP, REST / JSON 또는 기타 일 수 있습니다.

class Person {
    string Name;
    DateTime? DateOfBirth;
    ...
}

Identifier CreatePerson(Person);
Person GetPerson(Identifier);
void DeletePerson(Identifier);

업데이트는 어떻습니까? 자연스럽게해야 할 일은

void UpdatePerson(Identifier, Person);

그러나 업데이트 필드를 어떻게 지정 Person하시겠습니까?


내가 생각 해낼 수있는 솔루션 :

  • 당신은 항상 완전한 Person을 전달 하도록 요구할 수 있습니다. 즉, 클라이언트는 생년월일을 업데이트하기 위해 다음과 같은 것을 할 것입니다 :

    p = GetPerson(id);
    p.DateOfBirth = ...;
    UpdatePerson(id, p);
    

    그러나 Get과 Update 사이에 일종의 트랜잭션 일관성 또는 잠금이 필요합니다. 그렇지 않으면 다른 클라이언트가 병렬로 수행 한 다른 변경 사항을 덮어 쓸 수 있습니다. 이것은 API를 훨씬 더 복잡하게 만듭니다. 또한 다음 의사 코드 (JSON 지원 클라이언트 언어 가정) 때문에 오류가 발생하기 쉽습니다.

    UpdatePerson(id, { "DateOfBirth": "2015-01-01" });
    

    - 올바르게 보인다 -DateOfBirth를 변경할뿐만 아니라 다른 모든 필드를 null로 다시 설정합니다.

  • 인 모든 필드를 무시할 수 있습니다 null. 그러나 변경하지 않는 DateOfBirth 것과 의도적으로 null로 변경하는 것 사이의 차이점은 무엇입니까?

  • 서명을로 변경하십시오 void UpdatePerson(Identifier, Person, ListOfFieldNamesToUpdate).

  • 서명을로 변경하십시오 void UpdatePerson(Identifier, ListOfFieldValuePairs).

  • 전송 프로토콜의 일부 기능을 사용하십시오. 예를 들어, Person의 JSON 표현에 포함되지 않은 모든 필드를 무시할 수 있습니다. 그러나 일반적으로 JSON을 직접 파싱해야하며 라이브러리의 내장 기능 (예 : WCF)을 사용할 수 없습니다.

그 해결책 중 어느 것도 나에게 정말 우아해 보이지 않습니다. 분명히 이것은 일반적인 문제이므로 모든 사람이 사용하는 가장 좋은 방법은 무엇입니까?


왜 식별자가 사람의 일부가 아닌가? Person여전히 유지되지 않는 새로 생성 된 인스턴스의 경우 식별자가 지속성 메커니즘의 일부로 결정된 경우에는 null로 두십시오. 답에 관해서는 JPA는 버전 번호를 사용합니다. 버전 23을 읽으면 DB의 버전이 24 인 경우 항목을 업데이트 할 때 쓰기가 실패합니다.
SJuan76

PUTPATCH방법을 모두 허용하고 전달하십시오 . 를 사용 PATCH하는 PUT경우 전체 객체가 교체 된 상태 에서 전송 키만 교체해야합니다 .
Lode

답변:


8

이 개체에 대한 요구 사항으로 변경 사항을 추적하지 않은 경우 (예 : "사용자 John 이름 및 생년월일 변경") 가장 간단한 방법은 소비자로부터받은 개체로 DB의 전체 개체를 재정의하는 것입니다. 이 방법은 유선을 통해 전송되는 데이터가 약간 더 많지만 업데이트 전에는 읽지 않아도됩니다.

활동 추적 요구 사항이있는 경우 세상은 훨씬 더 복잡하므로 CRUD 조치에 대한 정보를 저장하는 방법과이를 가로채는 방법을 설계해야합니다. 그런 요구 사항이 없다면 다이빙을 원하지 않는 세상입니다.

별도의 거래로 가치를 재정의함에 따라 낙관적비관적 잠금에 대한 연구를 제안합니다 . 이러한 일반적인 시나리오를 완화합니다.

  1. user1이 오브젝트를 읽습니다.
  2. user2가 오브젝트를 읽습니다.
  3. user1이 쓴 객체
  4. user2에 의해 작성된 오브젝트 및 user1에 의해 변경된 변경 사항

각 사용자는 서로 다른 트랜잭션을 가지므로 표준 SQL을 사용합니다. 가장 일반적인 것은 낙관적 잠금입니다 (버전에 대한 설명에서 @ SJuan76에 의해 언급 됨). 버전은 DB에 기록되어 있으며 쓰기 중에 버전이 일치하면 먼저 DB를 살펴 봅니다. 버전이 일치하지 않으면 누군가 객체를 업데이트 한 것을 알고 있으며이 상황에 대해 소비자에게 오류 메시지로 응답해야합니다. 예,이 상황을 사용자에게 보여 주어야합니다.

낙관적 잠금 버전 비교를 위해 DB를 작성하기 전에 실제 레코드를 읽어야하므로 델타 로직 (변경된 값만 쓰기)을 구현하려면 쓰기 전에 추가 읽기 쿼리가 필요하지 않을 수 있습니다.

델타 로직을 사용하는 것은 소비자와의 계약에 크게 의존하지만, 소비자에게 가장 쉬운 것은 델타 대신 전체 페이로드를 구성하는 것입니다.


2

우리는 직장에서 PHP API를 가지고 있습니다. 필드가 JSON 객체로 전송되지 않으면 업데이트를 위해 NULL로 설정됩니다. 그런 다음 모든 것을 저장 프로 시저에 전달합니다. 저장 프로시 저는 field = IFNULL (input, field)로 모든 필드를 업데이트하려고 시도합니다. 따라서 하나의 필드 만 JSON 객체에 있으면 해당 필드 만 업데이트됩니다. 집합 필드를 명시 적으로 비우려면 field = ''가되어야합니다. 그러면 DB는 빈 문자열 또는 해당 열의 기본값으로 필드를 업데이트합니다.


3
아직 null이 아닌 필드를 의도적으로 null로 설정하는 방법은 무엇입니까?
Robert Harvey

모든 필드는 NOT NULL로 설정되므로 기본적으로 CHAR 필드는 ''이고 모든 정수 필드는 0입니다.
Jared Bernacchi

1

쿼리 문자열에 업데이트 된 필드 목록을 지정하십시오.

PUT /resource/:id?fields=name,address,dob Body { //resource body }

요청 본문에서 모델로 저장된 데이터 병합을 구현하십시오.

private ResourceModel MergeResourceModel(ResourceModel original, ResourceModel updated, List<string> fields)
{
    var comparer = new FieldComparer();

    foreach (
            var item in
            typeof (ResourceModel).GetProperties()
                    .Where(p => p.CustomAttributes.All(a => a.AttributeType != typeof (JsonIgnoreAttribute))))
    {
        if (fields.Contains(item.Name, comparer))
        {
            var property = typeof (ResourceModel).GetProperty(item.Name);
            property.SetValue(original, property.GetValue(updated));
        }
    }

    return original;
}
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.