Search code examples
javamappingmapstruct

MapStruct: multiple same flat sources to multiple same targets


I want to map this DTO :

public class BikeDTO {
    private String wheel_1_color;
    private String wheel_1_shape;

    private String wheel_2_color;
    private String wheel_2_shape;
}

To this Entity :

public class BikeEntity {
    private WheelEntity wheel1;
    private WheelEntity wheel2;
}

public class WheelEntity {
    private String color;
    private String shape;
}

So, I want to know if it's possible to use only one WheelMapper like this :

@Mapper
public interface BikeMapper {

    BikeMapper INSTANCE = Mappers.getMapper(BikeMapper.class);

    @Mapping(target="wheel1", source=".", qualifiedByName ="dtoToWheel")
    @Mapping(target="wheel2", source=".", qualifiedByName ="dtoToWheel")
    BikeEntity dtoToEntity(BikeDTO dto);

    @Named("dtoToWheel")
    default WheelEntity dtoToWheel(BikeDTO dto) {
        return WheelMapper.INSTANCE.dtoToWheel(dto);
    }
}

@Mapper
public interface WheelMapper {

    WheelMapper INSTANCE = Mappers.getMapper(WheelMapper.class);

    @Mapping(target="color", source="wheel_" + wheelNumber + "_color")
    @Mapping(target="shape", source="wheel_" + wheelNumber + "_shape")
    WheelEntity dtoToWheel(BikeDTO dto);
}

I'm stuck with wheelNumber and don't know how to set it.

I've written this piece of code to express my need, but I'll take any solution that helps me.

I'm probably thinking the wrong way and I'd like your help please?

PS: I obviously can't change the structure of my DTO or my entity.


Solution

  • i think it would be way easier for you to just implement a custom mapping function in your mapstruct mapper. you can also export it to a normal class or static function if you don't want to use mapstruct for this simple mapping function. because you don't really use any mapstruct functionality but it's way easier and more readable :)

    @Mapper
    public interface BikeMapper {
    
        default BikeEntity dtoToEntity(BikeDTO dto) {
            return new BikeEntity(
                new WheelEntity(dto.getWheel_1_Color(), dto.getWheel_1_Shape()),
                new WheelEntity(dto.getWheel_2_Color(), dto.getWheel_2_Shape())
            );
        }
    
    }
    
    // easiest way would be to use lombok or define an all args contructor for each of those two entities
    
    @AllArgsContructor 
    public class BikeEntity {
        private WheelEntity wheel1;
        private WheelEntity wheel2;
    }
    
    @AllArgsContructor
    public class WheelEntity {
        private String color;
        private String shape;
    }
    

    alternative answer if you want to use mapstructs codegen you could use expressions in @Mapping like this. a bit more code and unnecessary to let that generate through mapstruct. in both solutions you have to adjust the mapper if the bike has for example 3 wheels now. so decide for yourself :D

    @Mapper
    public interface BikeMapper {
    
        BikeMapper INSTANCE = Mappers.getMapper(BikeMapper.class);
    
        @Mapping(target = "wheel1", expression = "java(WheelMapper.INSTANCE.dtoToWheel1(dto))")
        @Mapping(target = "wheel2", expression = "java(WheelMapper.INSTANCE.dtoToWheel2(dto))")
        BikeEntity dtoToEntity(BikeDTO dto);
    }
    
    @Mapper
    public interface WheelMapper {
    
        WheelMapper INSTANCE = Mappers.getMapper(WheelMapper.class);
    
        @Mapping(target = "color", source = "wheel_1_color")
        @Mapping(target = "shape", source = "wheel_1_shape")
        WheelEntity dtoToWheel1(BikeDTO dto);
    
        @Mapping(target = "color", source = "wheel_2_color")
        @Mapping(target = "shape", source = "wheel_2_shape")
        WheelEntity dtoToWheel2(BikeDTO dto);
    }