无法使用 PUT 请求将实体内的值更新为 null

问题描述 投票:0回答:2

我有一种在我的 Java Spring boot 项目中使用 PUT 请求更新客户实体的方法。客户实体中有一个字段 notificationId。以下是该方法的声明。

customerEntity.setNotificationId(customerDto.getNotificationId() == null ? customerEntity.getNotificationId() : customerDto.getNotificationId());  

此语句按预期工作,但如果客户一旦将 notificationId 更新为:

12345
,然后再次想要更新为
null
,他将无法更新为
null
。我尝试了一些逻辑上的改变,但没有用。请提出更改建议。

java spring-boot spring-data-jpa
2个回答
2
投票

是的,这是将 JSON 映射到 Java DTO 时的标准问题。 JSON 知道不存在的键 (

{}
) 和
{"key"=null}
属性之间的区别。在 Java 中,这两个版本都将映射到
key = null
。您的 PUT 端点会忽略
null
值,以便您可以 PUT 部分更新并保持其他字段不变。这就是您发布的代码正在做的事情:

customerEntity.setNotificationId(customerDto.getNotificationId() == null ? customerEntity.getNotificationId() : customerDto.getNotificationId());

如果发布的值是

null
,它将使用(现有)实体中的值。更具可读性的变体是:

if (customerDto.getNotificationId() != null) {
  customerEntity.setNotificationId(customerDto.getNotificationId());
}

如何解决这个问题取决于你。有多种选择,包括但不限于:

  • 除了 PUT 端点之外,还创建一个 PATCH 端点,它对于如何处理
    null
    值具有不同的语义
  • 以不同的方式解析传入的 JSON,以便您知道客户端是否指定了显式
    null
    还是只是省略了该属性。

0
投票

发生这种情况是因为您无法区分未发送的字段和已发送为空的字段。如果您使用Optional,您可以区分它们。

将 DTO 类字段替换为可选

@Getter
@Setter
public class CustomerDTO {
    private Optional<String> notificationId;
    private Optional<String> notificationMessage;
    private Optional<LocalDateTime> notificationDate;
}

您可以保持实体类字段相同。

@Getter
@Setter
public class CustomerEntity {
    private String notificationId;
    private String notificationMessage;
    private LocalDateTime notificationDate;
}

如果

customerDto.getNotificationId()
为空,则表示客户端没有发送该字段;否则,客户端发送一个值,并且我们在可选中拥有该值。 如果我们也考虑空值,我们就无法像下一个那样获得这个值。

customerDTO.getNotificationId().orElse(null)
customerDTO.getNotificationMessage().orElse(null)
customerDTO.getNotificationData().orElse(null)

最后,你可以这样编写代码:

var customerEntity = new CustomerEntity();
if (customerDTO.getNotificationId() != null) {
    customerEntity.setNotificationId(customerDTO.getNotificationId().orElse(null));
}
if (customerDTO.getNotificationMessage() != null) {
    customerEntity.setNotificationMessage(customerDTO.getNotificationMessage().orElse(null));
}
if (customerDTO.getNotificationDate() != null) {
    customerEntity.setNotificationDate(customerDTO.getNotificationDate().orElse(null));
}

通过使用MapStruct,你可以避免空检查条件并使其更加优雅

@Mapper(componentModel = MappingConstants.ComponentModel.SPRING, nullValueCheckStrategy = NullValueCheckStrategy.ALWAYS)
public interface CustomerDtoMapping {
    @Mapping(source = "notificationId", target = "notificationId")
    @Mapping(source = "notificationMessage", target = "notificationMessage")
    @Mapping(source = "notificationDate", target = "notificationDate")
    CustomerEntity mapping(CustomerDTO customerDTO);

    default <T> T map(Optional<T> value) {
        return value.orElse(null);
    }
}
© www.soinside.com 2019 - 2024. All rights reserved.