27

I am writing simple blog web application using Spring MVC framework. I am willing to add DTO layer to my app.

I decided to use ModelMapper framework for conversion from Entity objects to DTO objects used in my views.

I have just one problem. On my main page, I am showing a list of posts on my blog. In my view, it's just list of Post (Entity) objects. I want to change it to pass a list of PostDTO objects to my view. Is there any way to map List of Post objects to List of PostDTO object with single method call? I was thinking about writing converter that will convert this but I am not sure it's a good way to do it.

Also, I am using Lists of Entities in few more places like administrative panel or comment below every Post on my page.

Link to code of my app on GitHub repository: repository

Andrew Nepogoda
  • 1,825
  • 17
  • 24
Gromo
  • 360
  • 2
  • 4
  • 13
  • If you have lazy relation to initialize, use `@transactional` or follow the [link](https://stackoverflow.com/a/42643297/13459296) to another answer. – alphkn May 03 '20 at 09:58

5 Answers5

63

You can create util class:

public class ObjectMapperUtils {

    private static final ModelMapper modelMapper;

    /**
     * Model mapper property setting are specified in the following block.
     * Default property matching strategy is set to Strict see {@link MatchingStrategies}
     * Custom mappings are added using {@link ModelMapper#addMappings(PropertyMap)}
     */
    static {
        modelMapper = new ModelMapper();
        modelMapper.getConfiguration().setMatchingStrategy(MatchingStrategies.STRICT);
    }

    /**
     * Hide from public usage.
     */
    private ObjectMapperUtils() {
    }

    /**
     * <p>Note: outClass object must have default constructor with no arguments</p>
     *
     * @param <D>      type of result object.
     * @param <T>      type of source object to map from.
     * @param entity   entity that needs to be mapped.
     * @param outClass class of result object.
     * @return new object of <code>outClass</code> type.
     */
    public static <D, T> D map(final T entity, Class<D> outClass) {
        return modelMapper.map(entity, outClass);
    }

    /**
     * <p>Note: outClass object must have default constructor with no arguments</p>
     *
     * @param entityList list of entities that needs to be mapped
     * @param outCLass   class of result list element
     * @param <D>        type of objects in result list
     * @param <T>        type of entity in <code>entityList</code>
     * @return list of mapped object with <code><D></code> type.
     */
    public static <D, T> List<D> mapAll(final Collection<T> entityList, Class<D> outCLass) {
        return entityList.stream()
                .map(entity -> map(entity, outCLass))
                .collect(Collectors.toList());
    }

    /**
     * Maps {@code source} to {@code destination}.
     *
     * @param source      object to map from
     * @param destination object to map to
     */
    public static <S, D> D map(final S source, D destination) {
        modelMapper.map(source, destination);
        return destination;
    }
}

And use it for your needs:

List<PostDTO> listOfPostDTO = ObjectMapperUtils.mapAll(listOfPosts, PostDTO.class);
Andrew Nepogoda
  • 1,825
  • 17
  • 24
  • Based on your code I wrote something similar, but easier to understand for me. And most importand, "mine". I am generally new to `Generics` in `Java`. Thank you very much for your help. – Gromo Dec 22 '17 at 18:12
  • I have a question, now how can we handle the custom mapping with this generics class. I need to map 2 very different objects which I can't do as it is with this class and I really liked the approach you are following! Thnx. – ajkush Oct 18 '18 at 20:42
  • @ajkush, take a look at modelmapper typemaps. – Andrew Nepogoda Oct 19 '18 at 08:22
  • Thank you @AndrewNepogoda ! Created a custom converter and added it to the mapper in ObjectMappingUtils. – ajkush Oct 19 '18 at 09:57
  • Thank's so much! Important solution for many situations! – Renato Vasconcellos Jul 22 '20 at 20:12
  • Great answer, but How can I ignore the field in the DTO, which don't need to map from Entity? – Hungnn Feb 09 '23 at 04:18
  • @AndrewNepogoda What is the benfit of using stream over using the "Type" and "TypeToken" to directly map the said lists using the modelmapper? – Drishti Jain Mar 09 '23 at 13:59
  • @DrishtiJain, I think there're no benefits. You can use both. "Type" and "TypeToken" looks even nicer. I voted this answer as well. – Andrew Nepogoda Jun 22 '23 at 09:17
  • @Hungnn, you can use type maps and skip properties like: `typeMap.addMappings(mapper -> mapper.skip(Destination::setName));` – Andrew Nepogoda Jun 22 '23 at 09:22
38

considering you have a list of Post Entity (postEntityList) and a PostDTO class, you can try following:

use the following imports to get the desired results

import org.modelmapper.ModelMapper;
import org.modelmapper.TypeToken;
import java.lang.reflect.Type;

use the below code

Type listType = new TypeToken<List<PostDTO>>(){}.getType();
List<PostDTO> postDtoList = modelmapper.map(postEntityList,listType);
Vignesh_A
  • 508
  • 1
  • 7
  • 19
André Carvalho
  • 511
  • 4
  • 9
17

Try the following simple way:

List<PostDTO> postDtoList = Arrays.asList(modelMapper.map(postEntityList, PostDTO[].class));
Jingchao Luan
  • 411
  • 4
  • 10
  • Why is util class being used as solution when this is clearly easier and out of the box without the need of custom code that does the same? – ivoronline Jan 06 '23 at 09:57
15

since you want to convert Entity to Dto, you can try the following one

List<PostDTO> entityToDto = modelMapper.map(postEntity, new TypeToken<List<PostDTO>>(){}.getType());
karthick S
  • 564
  • 1
  • 9
  • 18
4

Let's assume you are reading from the database, and want to convert from entities in the DB to DTOs

The service layer might be separated from the controller (always the best approach), then do this:

Service layer code:

@Override
    public List<PostDTO> convertEntityToDTOList( ){
            List<Post> post = postRepository.findAll();
            Type listType = new TypeToken<List<PostDTO>>(){}.getType();
            List<PostDTO> postDTOList = modelMapper.map(post, listType);
            return postDTOList;
        }

Then in your controller, add this:

public ResponseEntity<?> list(){
        List<PostDTO> postDTOList = iPost.convertEntityToDTOList();
        return new ResponseEntity<>(postDTOList, HttpStatus.OK);
    }

Note that the iPost is an interface that defines method. This is it:

public interface iPost {
   List<PostDTO> convertEntityToDTOList();
}

Credits to @André Carvalho

ken4ward
  • 2,246
  • 5
  • 49
  • 89