控制层使用DTO代替Entity

 

本文是关于如何在api请求体和响应体中使用DTO,以及如何在DTO和实体之间进行映射。

为什么使用DTO而不是实体?

  • 更新实体有时只更新实体的一部分,如果在请求体中使用实体,当您只传递实体中的部分字段时,其他字段将是实体的默认值或null。如果实体的字段太多,更新将变得非常麻烦。
  • 通过使用仅传递您需要的字段的不同DTO,它将使请求更加高效和简洁。
  • Spring MVC自动将请求参数绑定到bean,声明为使用@RequestMapping注释的方法的参数。由于这种自动绑定功能,可以在@RequestMapping带注释的方法的参数上提供一些意外的字段。参考自:https://rules.sonarsource.com/java/tag/spring/RSPEC-4684。 

你什么时候需要DTO?

通常我们需要在创建,更新实体时创建DTO或在api中返回一些json数据。

 1.创建一个实体

创建实体时,我们需要创建一个DTO来重现字段。在大多数情况下,DTO包含所有字段的实体,并且每个实体应该只有一个创建DTO。

我们应该只包含DTO中需要的字段,并将所有需要字段传递给DTO而不使用空值。 

例如,当您创建job时,如果创建job操作仅创建具有一些基本信息的作业,您只需要从请求传递必需的字段并创建DTO来处理传递的字段,

所以DTO只包含创建实体所必需的字段。在DTO中,使用验证来验证字段的完整性。例如

public class JobDto {
 
  private Long jobId;
 
  @NotBlank(message = "The job title couldn't be empty!")
  private String title;
 
  @NotBlank(message = "The job employment type couldn't be empty!")
  private String employmentType;
  ...
}

使用@Valid注释来验证它,如果验证没有通过,它将抛出异常:

@PostMapping(value = "/jobs")
@PreAuthorize("hasAuthority('create_job')")
public JobDetail createJob(@RequestBody @Valid final JobDto job) {
...
}

2.更新实体

更新实体时,如果更新是完全更新,我们可以使用在步骤1中创建的实体。如果更新是部分更新,我们需要创建新端点以进行更新并创建新的DTO以重新获取字段。

例如,当我们需要更新job的描述,但我们不想传递job的所有字段时,我们应该添加一个端点来处理更新,并创建一个DTO来重新接收该字段。例如

@Data
@NoArgsConstructor
public class JobDescriptionUpdatePojo {
  private String description;
}
 
@PatchMapping("jobs/{id}/job-description")
@PreAuthorize("hasPermission(#jobId,'change_job')")
public JobInfoDto updateJobDescription(@PathVariable final Long id,
    @RequestBody final JobDescriptionUpdatePojo jobDescriptionUpdatePojo) {
  final Job job = jobService.findJobById(id);
  return jobMapper.convertToJobInfoDto(jobService
      .updateJobDescription(JobDescriptionUpdatePojo, job));
}

3.返回json数据

当我们在api中返回json数据时,我们需要创建一个DTO来返回数据,而DTO应该只包含我们需要的字段。

使用MapStruct进行映射,我们需要将依赖项添加到项目的pom.xml文件中:

< dependency > 
  < groupId > org.mapstruct </ groupId > 
  < artifactId > mapstruct-jdk8 </ artifactId > 
  < version > 1.3.0.Final </ version > 
</ dependency > 

< dependency > 
  < groupId > org.mapstruct </ groupId > 
  < artifactId > mapstruct-processor </ artifactId > 
  < version >1.3.0.Final</ version > 
</ dependency >

配置映射器

这是所有映射器的全局配置,如果你真的需要使用不同的配置,请创建另一个。

@MapperConfig(

    componentModel = "spring",

    injectionStrategy = InjectionStrategy.CONSTRUCTOR,

    nullValuePropertyMappingStrategy = NullValuePropertyMappingStrategy.IGNORE,

    mappingInheritanceStrategy = MappingInheritanceStrategy.AUTO_INHERIT_FROM_CONFIG)

public interface Config {

 

  @Mapping(target = "id", ignore = true)

  BaseEntity convertToEntity(Object dto);

}

Config接口为mapper提供了一些基本配置。

  • 使用构造注入来注入映射器。
  • 对于映射,将忽略空值,仅对@MappingTarget注释有效。
  • 使任何DTO转换为实体忽略id字段。

用于映射的接口

一个实体对应一个映射器,定义以下方法以区分映射器的不同用法:

  • createFrom {DTOName}:将DTO转换为新实体,执行映射时将忽略“id”字段,因为它是最常见的用例作为主键。
  • updateFrom {DTOName}:将DTO转换为现有实体,实体param是存在的实体,此方法将用DTO的同名字段替换实体的字段。
  • convertToDto:使用相同的字段将实体转换为DTO。

@Mapper(config = Config.class)

public interface {Entity}Mapper {

 

  Entity createFrom{DTO1}(DTO1 dto1);

 

  void updateFrom{DTO2}(@MappingTarget Entity entity, DTO2 dto2);

 

  DTO3 convertTo{DTO3}(Entity entity);

 

  DTO4 convertTo{DTO4}(Entity entity);

 

  ...

}

映射器示例

  • 将@Mapper注释与您的自定义配置一起使用并根据需要导入值,MapStruct将自动生成接口的实现,就像Spring中的存储库一样。
  • 使用@Mapping注释来配置方法,注释为目标字段值提供表达式。
  • 使用@Mapping(target =“targetField”,source =“sourceField”)来指定映射字段,如果字段名称相同,则无需配置,它将自动映射。

@Mapper(

    config = Config.class,

    imports = {ClassNeedInExpression.class})

public interface JobMapper {

 

  @Mapping(target = "title", source = "jobTitle")

  @Mapping(target = "status", constant = "DRAFTING")

  @Mapping(

      target = "other field",

      expression = "java("some java code")"

      )

  Job createFromJobDto(JobDto jobDto);

 

  JobInfoDto convertToJobInfoDto(Job job);

}

示例使用映射器

  • 使用构造注入来注入您需要的映射器
  • 使用mapper转换实体或DTO

public JobInfoDto createJob(@RequestBody @Valid final JobDto job) {

  final Job persistentJob = jobMapper.createFromJobDto(job);

  // do other things with the converted job

  ...

  final Job newJob = jobService.save(persistentJob);

  // convert the new Job to return DTO

 

  final JobInfoDto jobDto = jobMapper.convertToJobInfoDto(newJob);

  return jobDto;

}

  • 3
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值