我有一种在我的 Java Spring boot 项目中使用 PUT 请求更新客户实体的方法。客户实体中有一个字段 notificationId。以下是该方法的声明。
customerEntity.setNotificationId(customerDto.getNotificationId() == null ? customerEntity.getNotificationId() : customerDto.getNotificationId());
此语句按预期工作,但如果客户一旦将 notificationId 更新为:
12345
,然后再次想要更新为 null
,他将无法更新为 null
。我尝试了一些逻辑上的改变,但没有用。请提出更改建议。
是的,这是将 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());
}
如何解决这个问题取决于你。有多种选择,包括但不限于:
null
值具有不同的语义null
还是只是省略了该属性。发生这种情况是因为您无法区分未发送的字段和已发送为空的字段。如果您使用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);
}
}