本文是关于如何在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 >
配置映射器
这是所有映射器的全局配置,如果你真的需要使用不同的配置,请创建另一个。
|
Config接口为mapper提供了一些基本配置。
- 使用构造注入来注入映射器。
- 对于映射,将忽略空值,仅对@MappingTarget注释有效。
- 使任何DTO转换为实体忽略id字段。
用于映射的接口
一个实体对应一个映射器,定义以下方法以区分映射器的不同用法:
- createFrom {DTOName}:将DTO转换为新实体,执行映射时将忽略“id”字段,因为它是最常见的用例作为主键。
- updateFrom {DTOName}:将DTO转换为现有实体,实体param是存在的实体,此方法将用DTO的同名字段替换实体的字段。
- convertToDto:使用相同的字段将实体转换为DTO。
|
映射器示例
- 将@Mapper注释与您的自定义配置一起使用并根据需要导入值,MapStruct将自动生成接口的实现,就像Spring中的存储库一样。
- 使用@Mapping注释来配置方法,注释为目标字段值提供表达式。
- 使用@Mapping(target =“targetField”,source =“sourceField”)来指定映射字段,如果字段名称相同,则无需配置,它将自动映射。
|
示例使用映射器
- 使用构造注入来注入您需要的映射器
- 使用mapper转换实体或DTO
|