Expected behavior
Upgrading form version 1.3.1.Final, where most mappings silently worked, this was hard to pinpoint.
I'd expect an exception if conflicting mappings are present in the list, instead of silent default to generated method.
Mapper with issue:
@Mapper(uses = PersonMapper.class)
public interface PersonInformationMapper {
@Mapping(target = "type", source = "type")
@Mapping(target = "relatedPerson.relatedInformation", ignore = true)
@Mapping(target = "relatedPerson", source = "relatedPerson")
@BeanMapping(ignoreByDefault = true)
PersonInformationDto toDto(PersonInformation personInformation);
Nested Mapper:
@Mapper(uses = PersonMappingHelper.class)
public interface PersonMapper {
@Mapping(target = "id", source = "id")
@Mapping(target = "name", source = "name")
@Mapping(target = "genderCode", source = "gender", qualifiedBy = PersonMappingHelper.Gender.class)
@BeanMapping(ignoreByDefault = true)
PersonDto toDto(Person person);
Clearly @Mapping(target = "relatedPerson.relatedInformation", ignore = true) is redundant here, however that should not have prevented PersonInformationMapper from using personMapper.toDto() to map related person. And not using it with no warning is the issue here.
Note: even qualifying toDto by name or annotation did not result in an exception.
Actual behavior
Here is the generated mapper implementation:
@Generated(
value = "org.mapstruct.ap.MappingProcessor",
date = "2026-07-14T11:33:11+0300",
comments = "version: 1.6.3, compiler: IncrementalProcessingEnvironment from gradle-language-java-8.8.jar, environment: Java 21.0.11 (Azul Systems, Inc.)"
)
public class PersonInformationMapperImpl implements PersonInformationMapper {
@Override
public PersonInformationDto toDto(PersonInformation personInformation) {
if ( personInformation == null ) {
return null;
}
PersonInformationDto personInformationDto = new PersonInformationDto();
personInformationDto.setRelatedPerson( personToPersonDto( personInformation.getRelatedPerson() ) );
personInformationDto.setType( personInformation.getType() );
return personInformationDto;
}
protected PersonDto personToPersonDto(Person person) {
if ( person == null ) {
return null;
}
PersonDto personDto = new PersonDto();
return personDto;
}
when removing the redundant @Mapping(target = "relatedPerson.relatedInformation", ignore = true) mapstruct correctly uses PersonMapper.toDto(Person) instead.
Steps to reproduce the problem
Currently build folder has the wrong interpretation of mapper class.
You can comment out line 15 PersonInformationMapper.class and run again, then PersonMapper will be used.
mapstruct-test.zip
MapStruct Version
MapStruct 1.6.3
Expected behavior
Upgrading form version 1.3.1.Final, where most mappings silently worked, this was hard to pinpoint.
I'd expect an exception if conflicting mappings are present in the list, instead of silent default to generated method.
Mapper with issue:
Nested Mapper:
Clearly
@Mapping(target = "relatedPerson.relatedInformation", ignore = true)is redundant here, however that should not have preventedPersonInformationMapperfrom usingpersonMapper.toDto()to map related person. And not using it with no warning is the issue here.Note: even qualifying toDto by name or annotation did not result in an exception.
Actual behavior
Here is the generated mapper implementation:
when removing the redundant
@Mapping(target = "relatedPerson.relatedInformation", ignore = true)mapstruct correctly usesPersonMapper.toDto(Person)instead.Steps to reproduce the problem
Currently build folder has the wrong interpretation of mapper class.
You can comment out line 15 PersonInformationMapper.class and run again, then PersonMapper will be used.
mapstruct-test.zip
MapStruct Version
MapStruct 1.6.3