查看原文
其他

【SFA官方译】:Spring REST API实体和DTO之间的转换

The following article is from 生活点亮技术 Author JackieTang

原文链接:https://www.baeldung.com/entity-to-and-from-dto-for-a-java-spring-application

作者: baeldung

译者:helloworldtang

1. 概览

在本教程中,我们将处理在Spring应用程序的内部实体和被发送到客户端的外部DTO(数据传输对象)之间的转换。

2. ModelMapper

首先,让我们看看用来执行实体-DTO转换的主要类库——ModelMapper

我们需要在pom.xml中添加这个依赖:

  1. <dependency>

  2.    <groupId>org.modelmapper</groupId>

  3.    <artifactId>modelmapper</artifactId>

  4.    <version>0.7.4</version>

  5. </dependency>

如果需要检查这个库是否有更新的版本, 请点击这里。

然后,我们将在Spring配置中定义ModelMapperbean:

  1. @Bean

  2. public ModelMapper modelMapper() {

  3.    return new ModelMapper();

  4. }

3. DTO

话分两头,接下来让我们来看看本例使用的DTO——PostDto

  1. public class PostDto {

  2.    private static final SimpleDateFormat dateFormat

  3.      = new SimpleDateFormat("yyyy-MM-dd HH:mm");

  4.    private Long id;

  5.    private String title;

  6.    private String url;

  7.    private String date;

  8.    private UserDto user;

  9.    public Date getSubmissionDateConverted(String timezone) throws ParseException {

  10.        dateFormat.setTimeZone(TimeZone.getTimeZone(timezone));

  11.        return dateFormat.parse(this.date);

  12.    }

  13.    public void setSubmissionDate(Date date, String timezone) {

  14.        dateFormat.setTimeZone(TimeZone.getTimeZone(timezone));

  15.        this.date = dateFormat.format(date);

  16.    }

  17.    // standard getters and setters

  18. }

请注意,上面与日期相关的两个方法,它们是用来处理客户端和服务器之间日期数据转换的:

  • getSubmissionDateConverted()方法将日期字符串转换为服务器所在时区中的日期,以便将其用于持久化Post实体

  • setSubmissionDate()方法是将DTO的日期设置为当前用户所在时区的Post日期

4. 服务层

现在让我们看一下服务层的操作——它显然是与实体(而不是DTO)一起工作:

  1. public List<Post> getPostsList(

  2.  int page, int size, String sortDir, String sort) {

  3.    PageRequest pageReq

  4.     = new PageRequest(page, size, Sort.Direction.fromString(sortDir), sort);

  5.    Page<Post> posts = postRepository

  6.      .findByUser(userService.getCurrentUser(), pageReq);

  7.    return posts.getContent();

  8. }

下面让我们来看看服务层上面的控制器层,这才是转换操作实际触发的地方。

5. 控制器层

现在,让我们来看一个标准的控制器,一个暴露Post资源的REST API。

我们将在这里展示一些简单的CRUD操作:创建、更新、获取一条和全部记录。考虑到操作非常简单,并且我们特别感兴趣的是实体-DTO转换方面

  1. @Controller

  2. class PostRestController {

  3.    @Autowired

  4.    private IPostService postService;

  5.    @Autowired

  6.    private IUserService userService;

  7.    @Autowired

  8.    private ModelMapper modelMapper;

  9.    @RequestMapping(method = RequestMethod.GET)

  10.    @ResponseBody

  11.    public List<PostDto> getPosts(...) {

  12.        //...

  13.        List<Post> posts = postService.getPostsList(page, size, sortDir, sort);

  14.        return posts.stream()

  15.          .map(post -> convertToDto(post))

  16.          .collect(Collectors.toList());

  17.    }

  18.    @RequestMapping(method = RequestMethod.POST)

  19.    @ResponseStatus(HttpStatus.CREATED)

  20.    @ResponseBody

  21.    public PostDto createPost(@RequestBody PostDto postDto) {

  22.        Post post = convertToEntity(postDto);

  23.        Post postCreated = postService.createPost(post));

  24.        return convertToDto(postCreated);

  25.    }

  26.    @RequestMapping(value = "/{id}", method = RequestMethod.GET)

  27.    @ResponseBody

  28.    public PostDto getPost(@PathVariable("id") Long id) {

  29.        return convertToDto(postService.getPostById(id));

  30.    }

  31.    @RequestMapping(value = "/{id}", method = RequestMethod.PUT)

  32.    @ResponseStatus(HttpStatus.OK)

  33.    public void updatePost(@RequestBody PostDto postDto) {

  34.        Post post = convertToEntity(postDto);

  35.        postService.updatePost(post);

  36.    }

  37. }

这是我们从Post实体到PostDto的转换:

  1. private PostDto convertToDto(Post post) {

  2.    PostDto postDto = modelMapper.map(post, PostDto.class);

  3.    postDto.setSubmissionDate(post.getSubmissionDate(),

  4.        userService.getCurrentUser().getPreference().getTimezone());

  5.    return postDto;

  6. }

这是从DTO到实体的转换:

  1. private Post convertToEntity(PostDto postDto) throws ParseException {

  2.    Post post = modelMapper.map(postDto, Post.class);

  3.    post.setSubmissionDate(postDto.getSubmissionDateConverted(

  4.      userService.getCurrentUser().getPreference().getTimezone()));

  5.    if (postDto.getId() != null) {

  6.        Post oldPost = postService.getPostById(postDto.getId());

  7.        post.setRedditID(oldPost.getRedditID());

  8.        post.setSent(oldPost.isSent());

  9.    }

  10.    return post;

  11. }

因此,正如您所看到的,在modelmapper库的帮助下,转换逻辑是快速且简单的——我们使用了modelMapper的map API,并且在不编写任何转换逻辑的情况下完成了数据转换。

6. 单元测试

最后,让我们做一个非常简单的测试,以确保实体和DTO之间的转换可以很好地工作:

  1. public class PostDtoUnitTest {

  2.    private ModelMapper modelMapper = new ModelMapper();

  3.    @Test

  4.    public void whenConvertPostEntityToPostDto_thenCorrect() {

  5.        Post post = new Post();

  6.        post.setId(Long.valueOf(1));

  7.        post.setTitle(randomAlphabetic(6));

  8.        post.setUrl("www.test.com");

  9.        PostDto postDto = modelMapper.map(post, PostDto.class);

  10.        assertEquals(post.getId(), postDto.getId());

  11.        assertEquals(post.getTitle(), postDto.getTitle());

  12.        assertEquals(post.getUrl(), postDto.getUrl());

  13.    }

  14.    @Test

  15.    public void whenConvertPostDtoToPostEntity_thenCorrect() {

  16.        PostDto postDto = new PostDto();

  17.        postDto.setId(Long.valueOf(1));

  18.        postDto.setTitle(randomAlphabetic(6));

  19.        postDto.setUrl("www.test.com");

  20.        Post post = modelMapper.map(postDto, Post.class);

  21.        assertEquals(postDto.getId(), post.getId());

  22.        assertEquals(postDto.getTitle(), post.getTitle());

  23.        assertEquals(postDto.getUrl(), post.getUrl());

  24.    }

  25. }

7. 总结

本文是关于如何在Spring REST API中使用modelmapper库来简化从实体到DTO以及从DTO到实体的转换,而不是重复造轮子。


推荐: 【SFA官方翻译】Spring Boot中配置Tomcat连接池

上一篇: 数据库中间件 MyCAT 源码分析 —— 跨库两表Join

关注公众号


    您可能也对以下帖子感兴趣

    文章有问题?点此查看未经处理的缓存