Entidade para conversão de DTO para uma API REST do Spring

Entidade para conversão de DTO para uma API REST do Spring

1. Visão geral

Neste tutorial, lidaremos com as conversões que precisam acontecerbetween the internal entities of a Spring application and the external DTOs (objetos de transferência de dados) que são publicadas de volta para o cliente.

Leitura adicional:

Um guia para mapear com o Dozer

O Dozer é um mapeador Java Bean para Java Bean que copia dados de um objeto para outro, atributo por atributo, suporta mapeamento entre nomes de atributos, faz conversão de tipo e muitas outras coisas.

Read more

Guia rápido do MapStruct

Um guia rápido e prático para usar o MapStruct

Read more

2. Model Mapper

Vamos começar apresentando a biblioteca principal que vamos usar para realizar a conversão de entidade-DTO -ModelMapper.

Precisamos dessa dependência empom.xml:


    org.modelmapper
    modelmapper
    2.3.2

Para verificar se há alguma versão mais recente desta biblioteca,go here.

Em seguida, definiremos o beanModelMapper em nossa configuração Spring:

@Bean
public ModelMapper modelMapper() {
    return new ModelMapper();
}

3. O DTO

A seguir, vamos apresentar o lado DTO desse problema bilateral -Post DTO:

public class PostDto {
    private static final SimpleDateFormat dateFormat
      = new SimpleDateFormat("yyyy-MM-dd HH:mm");

    private Long id;

    private String title;

    private String url;

    private String date;

    private UserDto user;

    public Date getSubmissionDateConverted(String timezone) throws ParseException {
        dateFormat.setTimeZone(TimeZone.getTimeZone(timezone));
        return dateFormat.parse(this.date);
    }

    public void setSubmissionDate(Date date, String timezone) {
        dateFormat.setTimeZone(TimeZone.getTimeZone(timezone));
        this.date = dateFormat.format(date);
    }

    // standard getters and setters
}

Observe que os 2 métodos relacionados à data personalizada lidam com a conversão de datas entre o cliente e o servidor:

  • O métodogetSubmissionDateConverted() converte String de data em uma data no fuso horário do servidor para usá-la na entidade Post persistente

  • O métodosetSubmissionDate() é definir a data do DTO para a data da postagem no fuso horário do usuário atual.

4. A Camada de Serviço

Vejamos agora uma operação de nível de serviço - que obviamente funcionará com a Entidade (não o DTO):

public List getPostsList(
  int page, int size, String sortDir, String sort) {

    PageRequest pageReq
     = PageRequest.of(page, size, Sort.Direction.fromString(sortDir), sort);

    Page posts = postRepository
      .findByUser(userService.getCurrentUser(), pageReq);
    return posts.getContent();
}

A seguir, vamos dar uma olhada na camada acima do serviço - a camada do controlador. É aqui que a conversão realmente acontecerá também.

5. A camada do controlador

Vamos agora dar uma olhada em uma implementação de controlador padrão, expondo a API REST simples para o recursoPost.

Vamos mostrar aqui algumas operações CRUD simples: criar, atualizar, obter um e obter todos. E como as operações são bastante diretas,we are especially interested in the Entity-DTO conversion aspects:

@Controller
class PostRestController {

    @Autowired
    private IPostService postService;

    @Autowired
    private IUserService userService;

    @Autowired
    private ModelMapper modelMapper;

    @RequestMapping(method = RequestMethod.GET)
    @ResponseBody
    public List getPosts(...) {
        //...
        List posts = postService.getPostsList(page, size, sortDir, sort);
        return posts.stream()
          .map(post -> convertToDto(post))
          .collect(Collectors.toList());
    }

    @RequestMapping(method = RequestMethod.POST)
    @ResponseStatus(HttpStatus.CREATED)
    @ResponseBody
    public PostDto createPost(@RequestBody PostDto postDto) {
        Post post = convertToEntity(postDto);
        Post postCreated = postService.createPost(post));
        return convertToDto(postCreated);
    }

    @RequestMapping(value = "/{id}", method = RequestMethod.GET)
    @ResponseBody
    public PostDto getPost(@PathVariable("id") Long id) {
        return convertToDto(postService.getPostById(id));
    }

    @RequestMapping(value = "/{id}", method = RequestMethod.PUT)
    @ResponseStatus(HttpStatus.OK)
    public void updatePost(@RequestBody PostDto postDto) {
        Post post = convertToEntity(postDto);
        postService.updatePost(post);
    }
}

E aqui estáour conversion from Post entity to PostDto:

private PostDto convertToDto(Post post) {
    PostDto postDto = modelMapper.map(post, PostDto.class);
    postDto.setSubmissionDate(post.getSubmissionDate(),
        userService.getCurrentUser().getPreference().getTimezone());
    return postDto;
}

E aqui está a conversãofrom DTO to an entity:

private Post convertToEntity(PostDto postDto) throws ParseException {
    Post post = modelMapper.map(postDto, Post.class);
    post.setSubmissionDate(postDto.getSubmissionDateConverted(
      userService.getCurrentUser().getPreference().getTimezone()));

    if (postDto.getId() != null) {
        Post oldPost = postService.getPostById(postDto.getId());
        post.setRedditID(oldPost.getRedditID());
        post.setSent(oldPost.isSent());
    }
    return post;
}

Então, como você pode ver, com a ajuda do mapeador de modelo,the conversion logic is quick and simple - estamos usando a APImap do mapeador e obtendo os dados convertidos sem escrever uma única linha de lógica de conversão.

6. Teste de Unidade

Finalmente, vamos fazer um teste muito simples para garantir que as conversões entre a entidade e o DTO funcionem bem:

public class PostDtoUnitTest {

    private ModelMapper modelMapper = new ModelMapper();

    @Test
    public void whenConvertPostEntityToPostDto_thenCorrect() {
        Post post = new Post();
        post.setId(Long.valueOf(1));
        post.setTitle(randomAlphabetic(6));
        post.setUrl("www.test.com");

        PostDto postDto = modelMapper.map(post, PostDto.class);
        assertEquals(post.getId(), postDto.getId());
        assertEquals(post.getTitle(), postDto.getTitle());
        assertEquals(post.getUrl(), postDto.getUrl());
    }

    @Test
    public void whenConvertPostDtoToPostEntity_thenCorrect() {
        PostDto postDto = new PostDto();
        postDto.setId(Long.valueOf(1));
        postDto.setTitle(randomAlphabetic(6));
        postDto.setUrl("www.test.com");

        Post post = modelMapper.map(postDto, Post.class);
        assertEquals(postDto.getId(), post.getId());
        assertEquals(postDto.getTitle(), post.getTitle());
        assertEquals(postDto.getUrl(), post.getUrl());
    }
}

7. Conclusão

Este foi um artigo sobresimplifying the conversion from Entity to DTO and from DTO to Entity in a Spring REST API, usando a biblioteca model mapper em vez de escrever essas conversões manualmente.

O código-fonte completo dos exemplos está disponível emGitHub project.