Search code examples
javastreamdtomapstruct

How to call MapStruct method from interface in java stream


I recently started using the MapStruct mapping tool in a project. In the past, for mapping DTO -> Entity and vice versa I used custom mapper like:

public static CustomerDto toDto(Customer customer) {

    return isNull(customer)
        ? null
        : CustomerDto.builder()
            .id(customer.getId())
            .name(customer.getName())
            .surname(customer.getSurname())
            .phoneNumber(customer.getPhoneNumber())
            .email(customer.getEmail())
            .customerStatus(customer.getCustomerStatus())
            .username(customer.getUsername())
            .NIP(customer.getNIP())
            .build();
  }

In case when I was trying to get one single Optional object after all I was able to map my entity to dto in the following way:

public Optional<CustomerDto> findOneById(final long id) {
    return customerRepository.findById(id).map(CustomerMapper::toDto);
  }

Currently, as I mentioned before I am using mapStruct and the problem is that my mapper it's, not class, it's the interface like:

@Mapper
public interface CommentMapper {

  @Mappings({
      @Mapping(target = "content", source = "entity.content"),
      @Mapping(target = "user", source = "entity.user")
  })
  CommentDto commentToCommentDto(Comment entity);

  @Mappings({
      @Mapping(target = "content", source = "dto.content"),
      @Mapping(target = "user", source = "dto.user")
  })
  Comment commentDtoToComment(CommentDto dto);

}

I want to know if it possible to use somehow this interface method in stream gentle to map my value without wrapping values like:

public Optional<CommentDto> findCommentById(final long id) {

    Optional<Comment> commentById = commentRepository.findById(id);
    return Optional.ofNullable(commentMapper.commentToCommentDto(commentById.get()));
}

Thanks for any help.


Solution

  • Access the mapper like:

    private static final YourMapper MAPPER = Mappers.getMapper(YourMapper.class);
    
    final Optional<YourEntity> optEntity = entityRepo.findById(id);
    return optEntity.map(MAPPER::toDto).orElse(null);
    

    Basically we do a similar thing with enumerations

     @Mapping(target = "type", expression = "java(EnumerationType.valueOf(entity.getType()))")
    

    you can define java expressions in your @Mapping annotation

    @Mapping(target = "comment", expression = "java(commentMapper.commentToCommentDto(commentRepository.findById(entity.commentId).orElse(null)))"
    

    Otherwise you should be able to make use of a

    class CommentMapper { ... }
    

    which you automatically can refer with

    @Mapper(uses = {CommentMapper.class})
    

    your implementation will detect the commentEntity and Dto and will automatically use the CommentMapper.

    A MapStruct mapper is workling like: Shit in Shit out, so remember your entity needs the commentEntity so the dto can has the commentDto.

    EDIT

    2nd solution could be using:

    @BeforeMapping
    default void beforeMappingToDTO(Entity source, @MappingTarget Dto target) {
        target.setComment(commentMapper.commentToCommentDto(commentRepository.findById(entity.commentId).orElse(null)));
    }