I just released Module 10 in the Master Class of my "REST With Spring" Course:

>> THE "REST WITH SPRING" CLASSES

1. Overview

In this tutorial, we’ll handle the conversions that need to happen between the internal entities of a Spring application and the external DTOs (Data Transfer Objects) that are published back to the client.

Further reading:

A Guide to Mapping With Dozer

Dozer is a Java Bean to Java Bean mapper that copies data from one object to another, attribute by attribute, supports mapping between attribute names, does type conversion, and many other things.

Read more

Quick Guide to MapStruct

A quick and practical guide to using MapStruct

Read more

2. Model Mapper

Let’s start by introducing the main library that we’re going to use to perform this entity-DTO conversion – ModelMapper.

We will need this dependency in the pom.xml:

<dependency>
    <groupId>org.modelmapper</groupId>
    <artifactId>modelmapper</artifactId>
    <version>0.7.4</version>
</dependency>

To check if there’s any newer version of this library, go here.

We’ll then define the ModelMapper bean in our Spring configuration:

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

3. The DTO

Next, let’s introduce the DTO side 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);
    }

    // standard getters and setters
}

Note that the 2 custom date related methods handle the date conversion back and forth between the client and the server:

  • getSubmissionDateConverted() method converts Date String into a date in server’s timezone to use it in persisting Post entity
  • setSubmissionDate() method is to set DTO’s date to Post’s Date in current user timezone.

4. The Service Layer

Let’s now look at a service level operation – which will obviously work with the Entity (not the DTO):

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

We’re going to have a look at the layer above service next – the controller layer. This is where the conversion will actually happen as well.

5. The Controller Layer

Let’s now have a look at a standard controller implementation, exposing the simple REST API for the Post resource.

We’re going to show here a few simple CRUD operations: create, update, get one and get all. And given the operations are pretty straightforward, 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<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);
    }
}

And here is 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;
}

And here is the conversion from 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;
}

So, as you can see, with the help of the model mapper, the conversion logic is quick and simple – we’re using the map API of the mapper and getting the data converted without writing a single line of conversion logic.

6. Unit Testing

Finally, let’s do a very simple test to make sure the conversions between the entity and the DTO work well:

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. Conclusion

This was an article on simplifying the conversion from Entity to DTO and from DTO to Entity in a Spring REST API, by using the model mapper library instead of writing these conversions by hand.

The Master Class of my "REST With Spring" Course is finally out:

>> CHECK OUT THE CLASSES

  • Hi,
    Thanks for the article.
    I just want to know how it works if it has child collection and bi-directional mapping such as Post has List comments and Comment has Post reference?

    • Hey Siva – that’s definitely an interesting question. For now this is done manually, but exploring alternatives with the ModelMapper is now on the TODO list, so if it’s supported, I’ll add a new section to this article.
      Cheers,
      Eugen.

      • Puneet Pandey

        I think @JsonManagedReference and @JsonBackReference would manage bi-directional child collection mapping.

        @JsonManagedReference
        private List comments;

        @JsonBackReference
        private Messafes messages;

  • Richard

    SimpleDateFormat (and all java.text.Format subclasses) is not thread safe, so you cannot safely use a static

    • Hey Richard – nice catch, looking into it. Cheers,
      Eugen.

  • Yasitha

    Just want to know your opinion about “why conversion happens in controller layer? ”
    What is the good practice?
    1. Convert in the service layer and return DTOs to controller, so controller just return DTOs
    2. As you implemented, Service returns model object and controller does the conversion.

    Which one is the best approach?

    • Hey Yasitha – that will of course depend on what your architecture looks like. Typically, that’s where these responsibilities exist – the service layer doesn’t know about DTOs, so the Controller layer handles the conversion before calling the service – that way you can cleanly separate the responsibilities.
      However, there’s no idiomatic way to go here – it’s simply a matter of what fits best with your architecture. For example, if you controller already has a number of other responsibilities, you may want to push this one down to the service. If it doesn’t, then this one is a good, simple responsibility for it.
      Hope it helps. Cheers,
      Eugen.

    • Given Nyauyanga

      Well for me I like my controllers clean and tidy, so I made sure that all my services return a DTO and never an entity. So after doing what needs to be done in a service method, I would convert the entity object and return a DTO 🙂 It really looks clean. Also I created a conversion service bean that has many methods for different DTO conversions. If I needed to use the bean, I would just Autowire it in the service and use the methods I need. This also made my services clean.

  • Saurabh

    How should we implement the bidirectional mapping in case we are using custom propertyMap

    • I’ll have to look into it to see exactly what can be done there.
      Feel free to open an issue over on github (and include test that exemplifies the problem) – and I’ll have a look.
      Cheers,
      Eugen.

  • Kanagha

    Is using a DTO similar to the concept where we do not want to expose model to the end user and just exposing a view version of it instead?
    In that case, it must not have any setters. Just a constructor with the entity object as input param.

    • In a way. A DTO is indeed all about separation of concerns and is the next logic step if you’re exposing your actual entities (which is not a good idea for quite a few reasons).
      What I’m not clear on is the part with “no setters and getters” – can you elaborate on that aspect?
      Cheers,
      Eugen.

      • Kanagha

        Thanks for the clarification! What I meant is was, a DTO doesn’t require any setter methods. It can just have a constructor that takes the entity object as input.

        • Sure, that’s perfectly fine. Just be aware of the fact that some frameworks (such as Jackson) will use setters and getters, so you may have to still have them defined on your DTO even if you’re not using them.

  • Ranjan

    I am getting the below exception. Can anyone please help me.

    Caused by: org.springframework.beans.factory.BeanCreationException: Could not autowire field: org.modelmapper.ModelMapper com.ibm.calypso.controller.RestGroupOfCompaniesController.modelMapper; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type [org.modelmapper.ModelMapper] found for dependency: expected at least 1 bean which qualifies as autowire candidate for this dependency. Dependency annotations: [email protected](required=true)}

    • Hey Ranjan – let me clarify – you’re getting this exception when you’re running one my projects from Github? Or is it happening in your own code?
      If it’s on the tutorials on github, definitely open an issue over there and I’ll have a look.
      If it’s in your own code, then my suggestion is to follow up with me over email.
      Cheers,
      Eugen.

    • Rafael Reis

      make sure that you instantiated the ModelMapper.

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

  • Raul Mordillo Lluva

    Hi!
    Thanks for your post! I always try to put all logic, even conversion to DTO’s in my service layer but you made a good point about separation of concerns so I will think about it and change my mind and architecture for further implementations.
    That been said, when you create an entity from A DTO, is that entity somehow detached from the jpa context? or you just simply and always store/update it with save?
    Am I mixing ideas here?
    thanks in advance

    • Given Nyauyanga

      The entity itself is attached to the jpa context of course but when updating you have to do a primary read to get a context entity object and update that entity using your DTO then merge. Remember that you need an ID or any other unique attribute to do the primary read. So a DTO is to make sure the client has what it only needs. If say you have 20 attributes in an entity and u only need 5 of those, create a DTO for those. I may have went a bit further but I hope you are answered.

      • Raul Mordillo Lluva

        Thanks to you as well.

    • Hey Raul,
      When you’re creating the entity from the DTO, it depends on the implementation. For example, let’s say that you first retrieve the old value of the entity and then set some of the new details from the DTO on it. In that case, the value is attached and you can work with it as such.
      If you’re doing something different though, then the value is probably going to not be attached. So, it simply depends on the particulars of your implementation.
      And no, you’re not mixing ideas – it’s definitely a relevant question.
      Cheers,
      Eugen.

      • Raul Mordillo Lluva

        Thanks for your reply and remarks

  • Sasha Papchenko

    This is just insane. One resource strictly prohibits to return models from Service layer. And suggests to return DTOs from services. Another resource claims that services does not know about DTO – so they need return actually models(domain objects). Other people don`t make difference between DTOs and ViewModels. I am really tired ) Just want to find out great augmented resource where i can find answers to my questions.

    • Hey Sasha,
      First, let me make sure I fully understand the context of what you’re saying here. I’m assuming you’re talking about different articles in general, not articles here on Baeldung.
      Second – the reason you’ll see different ways of dealing with this problem is very simple. There are different ways to deal with the problem.
      Basically, you can decide to do the conversion manually or automatically. You can decide to do in in one layer of your system – or in another.
      There isn’t a ONE way to do it.
      The important thing is of course to pick the way that actually makes sense for the system you’re building, understand the trade offs and then do it consistently.
      Hope that helps. Cheers,
      Eugen.

  • VladSerhiychuk

    Hi!
    Where did the userService in convertToDto ?

    • Where did the service come from? It’s wired into the controller (given that we’re talking about a Spring application). But it’s nothing special – just a standard bean.
      Hope this clears things up. Cheers,
      Eugen.

  • strike freedom

    Isn’t there a performance hit using this mapper vs hardcoding the conversion? Is it inspecting the dto and entity for each time it converts?

    • Grzegorz Piwowarek

      Yes, obviously there will be some performance hit but this is usually the case when you want your job to be done automatically. If you notice that your performance is not good enough then I would start investigating and I highly doubt that the automatic DTO-model conversion would be an actual bottleneck. Cheers!

  • Milorad Vojnovic

    How to model DTOs if I have inheritance in Entities, with or without inheritance?

    • Grzegorz Piwowarek

      Without inheritance. At the end of the day it’s simpler and allows you to avoid many potential problems. You can either play with “flattening” an inheritance structure or use composition instead