1
votes

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.

2
Optional = A container object which may or may not contain a non-null value. Please don't return it as a result of a method "findById" if there is no comment just return null! Comment comment = commentRepository.findById(id).orElse(null); - Spektakulatius
MapStructs is like lombok a pre processed interface which is generating an Impl class. - Spektakulatius

2 Answers

3
votes

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)));
}
2
votes

@Spektakulatius answer solved a problem.

To reach a goal I made a few steps: First of all, I created an object of my mapper to use it in a java stream:

private CommentMapper commentMapper = Mappers.getMapper(CommentMapper.class);

In the last step I used my object commentMapper like:

public Optional<CommentDto> findCommentById(final long id) {
    return commentRepository.findById(id).map(CommentMapper.MAPPER::commentToCommentDto);
}

After all that completely gave me a possibility to use my custom MapStruct mapper in stream.