【问题标题】:How to patch Dto to Entity?如何将 Dto 修补到实体?
【发布时间】:2023-03-19 12:52:01
【问题描述】:

我有一个接受 dto 对象的控制器。我需要更改 dto 对象中存在的字段。

@PatchMapping(value = "/update/{uuid}")
    public ResponseEntity<UserDto> update(
            @RequestBody UserDto userDto,
            @PathVariable("uuid")UUID uuid) throws UserNotFoundException {           
        User updatedUser = userService.update(
                userMapper.userDtoToUser(userDto),
                uuid
        );           
        return .....
    }

但 userService 只能接受实体。我需要使用映射器 dto -> 实体。但是实体不能有 dto 中的空字段(假设您只需要更改一个字段)。在这种情况下该怎么办?我知道控制器不应该包含逻辑

【问题讨论】:

  • 您是否考虑过将 userService 更改为期望 userDto 而不是 userEntity?服务层应该是处理转换逻辑的吧?
  • 在实体类中添加一个构造函数,它接受一个 DTO 对象作为唯一参数。
  • 你的问题是dto字段的转换还是验证?
  • 在 Spring MVC 中打补丁/部分更新并不容易。这里有一些讨论:stackoverflow.com/questions/37681555/…。这里可能的方法:cassiomolin.com/2019/06/10/using-http-patch-in-spring

标签: java spring spring-boot jpa dto


【解决方案1】:

解决此问题的两种可能方法。您必须更改服务方法以接受 dto 而不是实体,或者您必须创建一个实现 Converter 的 @Component 类,覆盖转换方法并在那里进行必要的字段更改,然后 @Autowire @987654322 @ 在你的控制器中调用genericConversionService.convert(userDto, User.class);

转换器应如下所示:

import org.springframework.core.convert.converter.Converter;
import org.springframework.stereotype.Component;

@Component
public class UserDtoToUser implements Converter<UserDto, User> {

    @Override
    public User convert(UserDto source) {
        User user = new User();
        // user.set ..... for all necessary fields
        return user;
    }

}

编辑

如果您想检查收到的字段的有效性,您可以简单地使用以下注释来确保您的数据正确:@NotBlank - 这会检查字段是否为空或空字符串,@987654324 @ - 检查字段是否为空,@NotEmpty - 检查字段是否为空或空集合。重要的是要记住 - 您必须在要验证的对象之前添加 @Valid 注释(旁注 - 如果是嵌套对象,您还需要将其添加到对象字段中)所以它看起来像这样@RequestBody @Valid UserDto userDto,

然后对于 dto,它应该看起来像这样:

import java.util.List;

import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.NotNull;

public class UserDto {
    @NotNull
    private Integer id;

    @NotBlank
    private String username;

    @NotBlank
    private String password;

    @NotEmpty
    private List<String> roles;
}

当然,将字段更改为您的 dto 中的任何内容。此外,如果您需要进行更多验证,还可以添加许多其他验证注释。

【讨论】:

  • 此映射如何处理 DTO 中的空值?它如何知道它们是 (a) 不存在于请求中还是 (b) 存在于请求中但显式无效?
  • 我最初认为问题是关于用户实体中的字段,这些字段在 dto 中不存在,但是是强制性的,但因为你不是第一个质疑这个和OP 仍未回复我发布的评论,我将编辑此答案以包括 dto 验证。
【解决方案2】:

正如你所说的控制器不应该包含逻辑,所以对于这个Spring有一个接口,接口是Validator

将验证视为业务逻辑有利有弊,Spring 提供了一种验证(和数据绑定)设计,不排除其中任何一个。具体来说,验证不应该与 Web 层绑定,应该易于本地化,并且应该可以插入任何可用的验证器。考虑到上述情况,Spring 提出了一个 Validator 接口,它既是基本的,又非常适用于应用程序的每一层。

这是你需要做的:

Spring 具有一个 Validator 接口,您可以使用它来验证对象。 Validator 接口使用 Errors 对象工作,因此在验证时,验证器可以向 Errors 对象报告验证失败。

我们有一个用于验证字段的 DTO:

public class Person {
    private String name;
    private int age;

    // the usual getters and setters...
}

要进行验证,我们必须实现 Validator 接口:

    public class PersonValidator implements Validator {

    /**
     * This Validator validates *just* Person instances
     */
    public boolean supports(Class clazz) {
        return Person.class.equals(clazz);
    }

    public void validate(Object obj, Errors e) {
        if (supports(obj.getClass())) {
            ValidationUtils.rejectIfEmpty(e, "name", "name.empty");
            Person p = (Person) obj;
            if (p.getAge() < 0) {
                e.rejectValue("age", "negativevalue");
            } else if (p.getAge() > 110) {
                e.rejectValue("age", "too.darn.old");
            }
        }
    }
}

如果 DTO 通过所有验证,您可以将 DTO 映射到实体对象

您可以在这里找到更多信息Spring Validator

【讨论】:

  • 虽然这已经过时了。您现在可以直接使用 @NotBlank 和 @Min 注释字段本身,只需在 UserDto userDto 请求正文参数前添加 @Valid。
  • 但是通过这种模式,皮卡丘可以验证该字段是否为空,我认为这是他所要求的,通过这种方式,您可以管理这种情况并拥有更多控制权
【解决方案3】:

您可以使用反射检查 null 属性和 BeanUtils 进行复制

在 Spring 中,这将是我检查空属性的方式

public static String[] getNullPropertyNames (Object source) {
    final BeanWrapper src = new BeanWrapperImpl(source);
    PropertyDescriptor[] pds = src.getPropertyDescriptors();

    Set<String> emptyNames = new HashSet<>();
    for(PropertyDescriptor pd : pds) {
        Object srcValue = src.getPropertyValue(pd.getName());
        if (srcValue == null) emptyNames.add(pd.getName());
    }
    return emptyNames.toArray(new String[0]);
}

然后进行复制

User updatedUser = new User();
BeanUtils.copyProperties(userDto, updatedUser, getNullPropertyNames(userDto));

【讨论】:

    猜你喜欢
    • 2017-06-12
    • 1970-01-01
    • 2021-06-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-09-17
    相关资源
    最近更新 更多