Digamos que você tenha algum tipo de estrutura de dados, que persiste em algum tipo de banco de dados. Para simplificar, vamos chamar essa estrutura de dados Person
. Agora você está encarregado de criar uma API CRUD, que permite que outros aplicativos criem, leiam, atualizem e excluam Person
s. Para simplificar, vamos supor que essa API seja acessada através de algum tipo de serviço da web.
Para as partes C, R e D do CRUD, o design é simples. Usarei notação funcional do tipo C # - a implementação pode ser SOAP, REST / JSON ou outra coisa:
class Person {
string Name;
DateTime? DateOfBirth;
...
}
Identifier CreatePerson(Person);
Person GetPerson(Identifier);
void DeletePerson(Identifier);
E a atualização? A coisa natural a fazer seria
void UpdatePerson(Identifier, Person);
mas como você especificaria quais campos Person
atualizar?
Soluções que eu poderia encontrar:
Você sempre pode exigir que uma Pessoa completa seja aprovada, ou seja, o cliente faria algo assim para atualizar a data de nascimento:
p = GetPerson(id); p.DateOfBirth = ...; UpdatePerson(id, p);
No entanto, isso exigiria algum tipo de consistência transacional ou bloqueio entre o Get e o Update; caso contrário, você poderá substituir outras alterações feitas em paralelo por outro cliente. Isso tornaria a API muito mais complicada. Além disso, é propenso a erros, uma vez que o pseudocódigo a seguir (assumindo uma linguagem de cliente com suporte a JSON)
UpdatePerson(id, { "DateOfBirth": "2015-01-01" });
- que parece correto - não apenas mudaria DateOfBirth, mas também redefiniria todos os outros campos para nulo.
Você pode ignorar todos os campos que são
null
. No entanto, como você faria a diferença entre não alterarDateOfBirth
e deliberadamente alterá-lo para nulo ?Mude a assinatura para
void UpdatePerson(Identifier, Person, ListOfFieldNamesToUpdate)
.Mude a assinatura para
void UpdatePerson(Identifier, ListOfFieldValuePairs)
.Use algum recurso do protocolo de transmissão: Por exemplo, você pode ignorar todos os campos não contidos na representação JSON da Pessoa. No entanto, isso geralmente requer a análise do JSON e não é possível usar os recursos internos da sua biblioteca (por exemplo, WCF).
Nenhuma das soluções parece realmente elegante para mim. Certamente, este é um problema comum, então qual é a solução de melhores práticas usada por todos?
fonte
Person
instâncias recém-criadas que ainda não são persistentes e, no caso de o identificador ser decidido como parte do mecanismo de persistência, deixe-o como nulo. Quanto à resposta, a JPA usa um número de versão; se você ler a versão 23, quando atualizar o item, se a versão no DB for 24, a gravação falhará.PUT
ePATCH
métodos. Ao usarPATCH
, apenas as chaves de envio devem ser substituídas, comPUT
o objeto inteiro substituído.Respostas:
Se você não possui o rastreamento de alterações como requisito para esse objeto (por exemplo, "O usuário John alterou o nome e a data de nascimento"), o mais simples seria substituir o objeto inteiro no DB por um que você receba do consumidor. Essa abordagem envolveria um pouco mais de envio de dados, mas você está evitando a leitura antes da atualização.
Se você possui um requisito de rastreamento de atividades. Seu mundo é muito mais complicado e você precisará projetar como armazenar informações sobre ações CRUD e como interceptá-las. Esse é o mundo em que você não deseja mergulhar se não tiver esse requisito.
De acordo com a substituição de valores por transações separadas, sugiro fazer uma pesquisa sobre bloqueio otimista e pessimista . Eles atenuam esse cenário comum:
Cada usuário tem uma transação diferente, portanto, SQL padrão com isso. O mais comum é o bloqueio otimista (também mencionado por @ SJuan76 no comentário sobre versões). Sua versão é seu registro no banco de dados e durante a gravação, você primeiro analisa o banco de dados se as versões corresponderem. Se as versões não corresponderem, você sabe que alguém atualizou o objeto nesse meio tempo e precisa responder com uma mensagem de erro ao consumidor sobre essa situação. Sim, você precisa mostrar esta situação ao usuário.
Observe que você precisa ler o registro real do DB antes de gravá-lo (para comparação otimizada da versão de bloqueio), portanto, implementar a lógica delta (gravar apenas valores alterados) pode não exigir uma consulta de leitura adicional antes da gravação.
A inserção da lógica delta depende muito do contrato com o consumidor, mas observe que o mais fácil para o consumidor é construir a carga útil completa em vez do delta também.
fonte
Temos uma API PHP em funcionamento. Para atualizações, se um campo não for enviado no objeto JSON, ele será definido como NULL. Em seguida, passa tudo para o procedimento armazenado. O procedimento armazenado tenta atualizar todos os campos com o campo = IFNULL (entrada, campo). Portanto, se apenas 1 campo estiver no objeto JSON, apenas esse campo será atualizado. Para esvaziar explicitamente um campo definido, precisamos ter o campo = '', o banco de dados atualiza o campo com uma string vazia ou com o valor padrão dessa coluna.
fonte
Especifique a lista de campos atualizados em Query String.
PUT /resource/:id?fields=name,address,dob Body { //resource body }
Implemente mesclar dados armazenados com o modelo do corpo da solicitação:
fonte