How to use decorated method in Mapstruct collection mapper?

Pierre Henry picture Pierre Henry · May 16, 2016 · Viewed 6.9k times · Source

I am using MapStruct to map from a JPA entity to a POJO DTO, in a Spring app with dependency injection.

I have added some additional processing of the DTO to a method in a decorator as specified in the doc.

It works fine for mapping a single entity. But I also have a mapping for a collection (set) of these entities and the method is called automatically when a collection of those entities is found in a relationship.

However the generated collection mapping method does not use the decorated method to map each entity, is just uses the "vanilla" generated method on the delegate. Here is the code of the generated method :

@Override
public Set<DimensionItemTreeDTO> missionSetToTreeDtoSet(Set<Mission> set)  {
    return delegate.missionSetToTreeDtoSet( set );
}

The delegate method itself is not aware of the decorator and calls the individual item mapping method on itself :

@Override
public Set<DimensionItemTreeDTO> missionSetToTreeDtoSet(Set<Mission> set) {
    if ( set == null ) {
        return null;
    }

    Set<DimensionItemTreeDTO> set__ = new HashSet<DimensionItemTreeDTO>();
    for ( Mission mission : set ) {
        set__.add( missionToTreeDto( mission ) ); //here the decorator is not called !
    }

    return set__;
}

...and the decorated method is never called for the items inside the collection.

Is there a way I can make Mapstruct use the decorator method in the collection mappings, short of writing the collection method manually in my decorator (which works but is verbose and defeats the purpose of having MapStruct in the first place which is not to have to write this kind of code) ?

Answer

Pierre Henry picture Pierre Henry · May 16, 2016

I found the solution to my problem : actually my use case was better suited for the MapStruct @AfterMapping methods, I used it and it is now working fine for all cases :

@Mapper
public abstract class ConstraintsPostProcessor {

    @Inject
    private UserService userService; // can use normal Spring DI here

    @AfterMapping
    public void setConstraintsOnMissionTreeDTO(Mission mission, @MappingTarget MissionDTO dto){ // do not forget the @MappingTarget annotation or it will not work
        dto.setUser(userService.getCurrentUser()); // can do any additional logic here, using services etc.
    }
}

And in the main mapper :

@Mapper(uses = {ConstraintsPostProcessor.class}) // just add the previous class here in the uses attribute
public interface DimensionMapper {
    ...
}