Conversion d'entité en DTO pour une API REST Spring

Haut REST

Je viens d'annoncer le nouveau cours Learn Spring , axé sur les principes de base de Spring 5 et Spring Boot 2:

>> VOIR LE COURS

1. Vue d'ensemble

Dans ce didacticiel, nous allons gérer les conversions qui doivent se produire entre les entités internes d'une application Spring et les DTO externes (Data Transfer Objects) qui sont publiés sur le client.

2. Model Mapper

Commençons par présenter la bibliothèque principale que nous allons utiliser pour effectuer cette conversion entité-DTO - ModelMapper .

Nous aurons besoin de cette dépendance dans le pom.xml :

 org.modelmapper modelmapper 2.3.5 

Pour vérifier s'il existe une version plus récente de cette bibliothèque, allez ici.

Nous définirons ensuite le bean ModelMapper dans notre configuration Spring:

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

3. Le DTO

Ensuite, introduisons le côté DTO de ce problème à deux faces - 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 } 

Notez que les deux méthodes personnalisées liées à la date gèrent la conversion de date entre le client et le serveur:

  • La méthode getSubmissionDateConverted () convertit la chaîne de date en une date dans le fuseau horaire du serveur pour l'utiliser dans l' entité Post persistante
  • setSubmissionDate () méthode consiste à définir la date de DTO Poste de date dans le fuseau horaire de l' utilisateur actuel.

4. La couche de service

Regardons maintenant une opération de niveau de service - qui fonctionnera évidemment avec l'entité (pas le 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(); }

Nous allons ensuite examiner la couche au-dessus du service - la couche contrôleur. C'est là que la conversion se produira également.

5. La couche contrôleur

Examinons maintenant une implémentation de contrôleur standard, exposant l'API REST simple pour la ressource Post .

Nous allons montrer ici quelques opérations CRUD simples: créer, mettre à jour, en obtenir une et tout obtenir. Et étant donné que les opérations sont assez simples, nous sommes particulièrement intéressés par les aspects de conversion Entity-DTO :

@Controller class PostRestController { @Autowired private IPostService postService; @Autowired private IUserService userService; @Autowired private ModelMapper modelMapper; @GetMapping @ResponseBody public List getPosts(...) { //... List posts = postService.getPostsList(page, size, sortDir, sort); return posts.stream() .map(this::convertToDto) .collect(Collectors.toList()); } @PostMapping @ResponseStatus(HttpStatus.CREATED) @ResponseBody public PostDto createPost(@RequestBody PostDto postDto) { Post post = convertToEntity(postDto); Post postCreated = postService.createPost(post)); return convertToDto(postCreated); } @GetMapping(value = "/{id}") @ResponseBody public PostDto getPost(@PathVariable("id") Long id) { return convertToDto(postService.getPostById(id)); } @PutMapping(value = "/{id}") @ResponseStatus(HttpStatus.OK) public void updatePost(@RequestBody PostDto postDto) { Post post = convertToEntity(postDto); postService.updatePost(post); } }

Et voici notre conversion d' entité Post en PostDto :

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

Et voici la conversion de DTO en entité :

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; }

Ainsi, comme vous pouvez le voir, avec l'aide du mappeur de modèle, la logique de conversion est rapide et simple - nous utilisons l' API de carte du mappeur et convertissons les données sans écrire une seule ligne de logique de conversion.

6. Tests unitaires

Enfin, faisons un test très simple pour nous assurer que les conversions entre l'entité et le DTO fonctionnent bien:

public class PostDtoUnitTest { private ModelMapper modelMapper = new ModelMapper(); @Test public void whenConvertPostEntityToPostDto_thenCorrect() { Post post = new Post(); post.setId(1L); 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(1L); 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. Conclusion

Il s'agissait d'un article sur la simplification de la conversion d'entité en DTO et de DTO en entité dans une API Spring REST , en utilisant la bibliothèque de mappage de modèles au lieu d'écrire ces conversions à la main.

Le code source complet des exemples est disponible dans le projet GitHub.

REST bas

Je viens d'annoncer le nouveau cours Learn Spring , axé sur les principes de base de Spring 5 et Spring Boot 2:

>> VOIR LE COURS