introduce

In this article, we’ll deal with the transformations that need to take place between the internal entities of Spring applications and the Dtos (Data transfer Objects) outside the client.

Model mapping

Let’s start by introducing the main library for the entity-to-DTO transformation to be performed — ModelMapper. We need to add the following dependencies to pom.xml:

<dependency>
    <groupId>org.modelmapper</groupId>
    <artifactId>modelmapper</artifactId>
    <version>2.3.2</version>
</dependency>
Copy the code

We then define the ModelMapper bean in the Spring configuration:

@Bean
public ModelMapper modelMapper(a) {
    return new ModelMapper();
}
Copy the code

DTO

Next, let’s introduce the DTO aspect of this two-sided problem — 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);
    }
 
    // getters and setters
}
Copy the code

Note that two custom date-dependent methods handle date-to-date conversions between client and server:

  • GetSubmissionDateConverted () method converts a Date string to the server time zone in the Date, to use it in the persistent Post entity
  • The setSubmissionDate() method is used to set the Date of the DTO to the Date of Post in the current user’s time zone

Service

Now let’s look at a Service layer operation — it will obviously work with entities (not Dtos) :

public List<Post> getPostsList(
  int page, int size, String sortDir, String sort) {
  
    PageRequest pageReq
     = PageRequest.of(page, size, Sort.Direction.fromString(sortDir), sort);
  
    Page<Post> posts = postRepository
      .findByUser(userService.getCurrentUser(), pageReq);
    return posts.getContent();
}
Copy the code

We will next look at the upper layer of the service, the Controller layer, where the transformation actually takes place.

Controller

Now let’s look at a standard Controller implementation that exposes a simple REST API for Post. We’ll show you a few simple CRUD operations here: Create, update, get one, and get All.

@Controller
class PostRestController {
 
    @Autowired
    private IPostService postService;
 
    @Autowired
    private IUserService userService;
 
    @Autowired
    private ModelMapper modelMapper;
 
    @RequestMapping(method = RequestMethod.GET)
    @ResponseBody
    public List<PostDto> getPosts(...). {
        / /...
        List<Post> 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); }}Copy the code

Here is our conversion from Post entities to PostDto:

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

This is the conversion from DTOS to entities:

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;
}
Copy the code

So, as you can see, the transformation logic is fast and simple with the help of the model mapping — we use the map API of the mapping to get the transformed data without writing any transformation logic.

Unit testing

Finally, let’s do a very simple test to ensure that the transformation between entities and Dtos works:

public class PostDtoUnitTest {
 
    private ModelMapper modelMapper = new ModelMapper();
 
    @Test
    public void whenConvertPostEntityToPostDto_thenCorrect(a) {
        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(a) {
        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()); }}Copy the code

conclusion

This is an article about simplifying entity-to-DTO and DTO-entity transformations in the Spring REST API by using a model mapping library rather than writing these transformations manually.

Welcome to follow my official account: Qulingfeng, for exclusive learning resources and daily dry goods push. If you are interested in my feature content, you can also follow my blog: sagowiec.com