luthier
luthier

Reputation: 2841

Use another MapStruct mapper only inside an expression clause

I have a mapper that, for a particular attribute of the target class, needs to choose one from a list of objects inside the source object, and map it using a differente mapper class.

Simplifying it a lot, the Game class contains a list of Transaction objects, and my GameMapper class looks like this:

@Component
@Mapper(injectionStrategy = InjectionStrategy.CONSTRUCTOR, uses = {TransactionMapper.class, EventMapper.class})
public interface GameMapper {

@Mapping(target = "transaction", 
    expression = "java(transactionMapper.transactionToDto(findTransactionForPlayer(game, idPlayer)))")
GameResultDto gameToGameResultDto(Game game, Long idPlayer);

// Some more methods

}

The thing is, EventMapper gets generated as a private final EventMapper eventMapper; attribute inside GameMapperImpl, but TransactionMapper is not, so the build fails because MapStruct cannot find transactionMapper.

My best guess is that this is due to no other methods in GameMapper using TransactionMapper explicitly, so Mapstruct decides that it's not needed and doesn't inject it in the implementation.

So... is there any way to force MapStruct to include the mappers in the uses clause, even it if looks like they are not being used, or any other way to work around this?

Upvotes: 9

Views: 13303

Answers (2)

Filip
Filip

Reputation: 21403

My best guess is that this is due to no other methods in GameMapper using TransactionMapper explicitly, so Mapstruct decides that it's not needed and doesn't inject it in the implementation.

That is correct. MapStruct will not inject mappers from Mapper#uses if they are not used by MapStruct.

What you could do is to use an abstract class.

e.g.

@Mapper(injectionStrategy = InjectionStrategy.CONSTRUCTOR, componentModel = "spring", uses = {TransactionMapper.class, EventMapper.class})
public abstract class GameMapper {

    @Autowired
    protected TransactionMapper transactionMapper;

    @Mapping(target = "transaction", 
        expression = "java(transactionMapper.transactionToDto(findTransactionForPlayer(game, idPlayer)))")
    public abstract GameResultDto gameToGameResultDto(Game game, Long idPlayer);

    // Some more methods

}

Upvotes: 13

luthier
luthier

Reputation: 2841

I managed to find a hacky solution. By adding a List<TransactionDto> dummy(List<Transaction> transaction); method to GameMapper, I have tricked MapStruct into thinking I'm really using TransactionMapper for something and it has successfully added it to GameMapperImpl.

I'm still open to non-hacky solutions, though :)

Upvotes: 2

Related Questions