Ahmed Aziz
Ahmed Aziz

Reputation: 427

Use Java 8 optional with Mapstruct

I have these two classes:

public class CustomerEntity {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private long id;
    private String firstName;
    private String lastName;
    private String address;
    private int age;
    private LocalDateTime createdAt;
    private LocalDateTime updatedAt;
}

and

public class CustomerDto {
    private Long customerId;
    private String firstName;
    private String lastName;
    private Optional<String> address;
    private int age;
}

The problem is that Mapstruct doesn't recognize the Optional variable "address".

Anyone has an idea how to solve it and let Mapstruct map Optional fields?

Upvotes: 10

Views: 19265

Answers (2)

tuulingo
tuulingo

Reputation: 89

New solution: return repository.findById(id).map(entity -> mapper.toDTO(entity));

Older solution: People seem to have problem after upgrading from mapstruct 1.3.1 to higher version, so the new workaround would be:

@Mapping(source = "child", target = "kid")
Target map(Source source);

    @Reference
    public static <T> T unwrapReference(Optional<T> optional) {
        return (optional != null && optional.isPresent()) ? optional.get() : null;
    }

expecting you have imported the mapper, you can use it something like this:

Optional.ofNullable(kidMapper.toKidDTO(KidMapper.unwrapReference(kidRepository.findById(chatId))));

answered by janinko in this issue: https://github.com/mapstruct/mapstruct/issues/2295

Upvotes: 1

Gustavo Passini
Gustavo Passini

Reputation: 2668

This is not yet supported out of the box by MapStruct. There is an open ticket on their GitHub asking for this functionality: https://github.com/mapstruct/mapstruct/issues/674

One way to solve this has been added in the comments of the same ticket: https://github.com/mapstruct/mapstruct/issues/674#issuecomment-378212135

@Mapping(source = "child", target = "kid", qualifiedByName = "unwrap")
Target map(Source source);

@Named("unwrap")
default <T> T unwrap(Optional<T> optional) {
    return optional.orElse(null);
}

As pointed by @dschulten, if you want to use this workaround while also setting the option nullValueCheckStrategy = NullValueCheckStrategy.ALWAYS, you will need to define a method with the signature boolean hasXXX() for the field XXX of type Optional inside the class which is the mapping source (explanation in the docs).

Upvotes: 17

Related Questions