0

I have a mapper where I'm trying to use a repository as context so I can fetch my object during the mapping. I spent a lot of time searching how context work and this is what I came up with

@Mapper(componentModel = MappingConstants.ComponentModel.SPRING, nullValueMappingStrategy = NullValueMappingStrategy.RETURN_DEFAULT)
public interface EnumValueMapper {
    @Mapping(target = "enumDefinition", source = "enumDefinitionId", qualifiedByName = "getEnumDefinition")
    EnumValue toEntity(EnumValueBean enumValueBean);

    @Mapping(target = "enumDefinitionId", source = "enumDefinition.name")
    EnumValueBean toBean(EnumValue enumValue);

    @Named("getEnumDefinition")
    static EnumDefinition getEnumDefinition(String enumDefinitionName, @Context IEnumDefinitiontDao enumDefinitionDao) {
        return enumDefinitionDao.findById(enumDefinitionName).orElse(null);
    }
}

The issue with this is that I get the error

Qualifier error. No method found annotated with @Named#value: [ getEnumDefinition ]. See https://mapstruct.org/faq/#qualifier for more info.

due to the compiler seeing @Context as a 2nd parameter and not finding a named "getEnumDefinition" that matches the 1 String parameter method. If I remove the @Context parameter the error disappears and the build succeed. I'm confused at what I'm doing wrong because I didn't see anyone explicitly giving a context inside a @Mapping, they just give their source and the name for the qualifiedByName and it finds the correct method despite the @Context.

Kpo
  • 147
  • 1
  • 10

1 Answers1

1

A mapper's method signature must provide the reference decorated with @Context annotation for a qualifiedByName method to use the context. You can also achieve providing the qualifiedByName method a reference to the enumDefinitionDao by auto-wiring the DAO in an abstract class via setter injection, as demonstrated below.

@Service
@AllArgsConstructor
public class DefinitionService {

    // Autowire Bean Approach
    private final EnumValueMapperWithAutowiredBean enumValueMapperWithAutowiredBean;

    // Context Approach
    private final IEnumDefinitionDao enumDefinitionDao;
    private final EnumValueMapperWithContext enumValueMapperWithContext;

    public EnumValue doLookupUsingContextApproach(EnumValueBean bean) {
        return enumValueMapperWithContext.toEntity(bean, enumDefinitionDao);
    }

    public EnumValue doLookupUsingAutowireBeanApproach(EnumValueBean bean) {
        return enumValueMapperWithAutowiredBean.toEntity(bean);
    }

}


@Mapper
public interface EnumValueMapperWithContext {

    @Mapping(target = "enumDefinition", source = "enumDefinitionId", qualifiedByName = "getEnumDefinition")
    EnumValue toEntity(EnumValueBean enumValueBean, @Context IEnumDefinitionDao enumDefinitionDao);

    @Mapping(target = "enumDefinitionId", source = "enumDefinition.name")
    EnumValueBean toBean(EnumValue enumValue);

    @Named("getEnumDefinition")
    default EnumDefinition getEnumDefinition(
        String enumDefinitionName,
        @Context IEnumDefinitionDao enumDefinitionDao) {
        return enumDefinitionDao.findById(enumDefinitionName).orElse(null);
    }

}


@Mapper
public abstract class EnumValueMapperWithAutowiredBean {

    protected IEnumDefinitionDao enumDefinitionDao;

    @Autowired
    public final void setEnumDefinitionDao(IEnumDefinitionDao enumDefinitionDao) {
        this.enumDefinitionDao = enumDefinitionDao;
    }

    @Mapping(target = "enumDefinition", source = "enumDefinitionId", qualifiedByName = "getEnumDefinition")
    public abstract EnumValue toEntity(EnumValueBean enumValueBean);

    @Mapping(target = "enumDefinitionId", source = "enumDefinition.name")
    public abstract EnumValueBean toBean(EnumValue enumValue);

    @Named("getEnumDefinition")
    EnumDefinition getEnumDefinition(String enumDefinitionName) {
        return enumDefinitionDao.findById(enumDefinitionName).orElse(null);
    }

}
Edwin M. Cruz
  • 362
  • 1
  • 8