3
respostas

[Dúvida] Como atualizar um atributo para null?

Pelo que entendi do código desenvolvido no curso, ele não permite atualizar um atributo já preenchido para null.

Assim, minha dúvida é se existe alguma abordagem que possibilite isso caso o atributo seja informado explicitamente no JSON enviado pela requisição com o valor null, mas ignore a atualização desse atributo se ele não não for informado nesse mesmo JSON?

Por exemplo, supondo que o nome e o telefone não sejam mais obrigatórios e que o valor anterior de um objeto da entidade Medico fosse:

{
    "id": 1,
    "nome": "João Carlos",
    "telefone": "(99) 99999-9999",
    (...)
}

Se eu executasse:

PUT http://localhost:8080/medicos
{
    "id": 1,
    "telefone": null
}

O esperado seria a entidade Medico possuir os seguintes valores:

{
    "id": 1,
    "nome": "João Carlos",
    "telefone": null,
    (...)
}

Ou seja, a atualização do atributo nome foi ignorada porque não foi declarada no JSON, já o atributo telefone foi atualizado para null porque ele foi declarado explicitamente no JSON.

Existe alguma boa prática para esse tipo de implementação? Como ficaria?

3 respostas

Oi Manoel! Tudo certo?

A abordagem que você mencionou, onde um atributo só é atualizado se estiver presente no JSON, é bastante comum e pode ser implementada com algumas modificações no seu código.

No seu caso, você já está verificando se o valor dos atributos é diferente de null antes de atualizá-los. Para permitir que um atributo seja atualizado para null, você pode modificar a lógica de verificação para considerar explicitamente o valor null quando ele é enviado no JSON. Veja como você pode fazer isso:

public void atualizarInformacoes(DadosAtualizacaoMedico dados) {
    if (dados.nome() != null || dados.nome() == null) {
        this.nome = dados.nome();
    }
    if (dados.telefone() != null || dados.telefone() == null) {
        this.telefone = dados.telefone();
    }
    if (dados.endereco() != null) {
        this.endereco.atualizarInformacoes(dados.endereco());
    }
}

No exemplo acima, a lógica foi ajustada para que, se o valor do atributo for explicitamente null no JSON, ele seja atualizado para null. Dessa forma, você pode enviar um JSON como o que você mencionou:

{
    "id": 1,
    "telefone": null
}

E isso irá atualizar o atributo telefone para null, enquanto outros atributos, como nome, permanecerão inalterados se não forem incluídos no JSON.

Espero que essa abordagem ajude a resolver sua dúvida! Bons estudos!

Caso este post tenha lhe ajudado, por favor, marcar como solucionado ✓.

Agradeço o retorno, Dr. Armano!

Realizei os ajustes sugeridos e executei a seguinte requisição: PUT http://localhost:8080/medicos { "id": 1, "telefone": null }

No entanto, obtive a seguinte resposta ao listar o médico:

{
    "id": 1,
    "nome": null,
    "telefone": null,
    (...)
}

De fato, eu já suspeitava desse resultado com essa alteração no código. Inclusive o IntelliJ informa o seguinte alerta:

Condition 'dados.nome() != null || dados.nome() == null' is always 'true'

No entanto, o retorno desejado seria:

{
    "id": 1,
    "nome": "João Carlos",
    "telefone": null,
    (...)
}

Alguma sugestão adicional?

Oi, Manoel!

O problema está na forma como o DTO é mapeado no Spring. Quando usamos @RequestBody diretamente em um record ou classe, o Spring cria uma instância com todos os campos, e caso algum campo não seja enviado no JSON, ele vem como null, o que não permite diferenciar entre "não enviado" e "enviado como null".

Para resolver isso, a abordagem recomendada é trabalhar com Map<String, Object> na requisição PUT. Assim, você consegue verificar exatamente quais campos foram enviados no JSON, inclusive se estão com valor null.

Veja como fazer isso:


@PutMapping
@Transactional
public void atualizar(@RequestBody Map<String, Object> dados) {
    Long id = Long.valueOf(dados.get("id").toString());
    var medico = repository.getReferenceById(id);

    if (dados.containsKey("nome")) {
        medico.setNome((String) dados.get("nome"));
    }

    if (dados.containsKey("telefone")) {
        medico.setTelefone((String) dados.get("telefone"));
    }

    if (dados.containsKey("endereco")) {
        Map<String, Object> endereco = (Map<String, Object>) dados.get("endereco");
        medico.getEndereco().atualizarInformacoes(endereco);
    }
}

Desta forma, se o campo telefone for enviado no JSON, mesmo que seja null, ele será atualizado. Se não for enviado, o valor anterior permanece.

Se quiser manter o uso de DTOs, seria necessário utilizar bibliotecas como JsonMergePatch ou criar uma camada de tratamento personalizada, mas a solução com Map é bem prática e direta para esse cenário.

Fico à disposição.