Mapstruct使用qualifiedByName映射含List的DTO时遇方法歧义错误求解
在使用MapStruct映射包含DTO列表的DTO时,指定qualifiedByName参数仍出现映射方法歧义错误,具体场景与问题如下:
相关代码实现
RoleDTO
public class RoleDTO extends AbstractDTO { // ... private List<DepartmentDTO> departments = new ArrayList<>(); // 这是一个列表! }
抽象映射器GenericMapper
public interface GenericMapper<E extends AbstractEntity, D extends AbstractDTO> { @IterableMapping(qualifiedByName = "toEntity") @Named("toEntities") @NotNull List<E> toEntities(@NonNull List<D> dtos); @Named("toEntity") @NotNull default E toEntity(@NonNull D dto) { return toEntityCycleAvoiding(dto, new CycleAvoidingMappingContext()); } }
RoleMapper
@Mapper(uses = {DepartmentMapper.class}) public interface RoleMapper extends GenericMapper<Role, RoleDTO> { @Mapping(target = "departments", qualifiedByName = "toEntitiesLightweight") // 已指定对应方法 @Named("toEntityLightweight") Role toEntityLightweight(@NonNull RoleDTO role); }
DepartmentMapper
@Mapper(uses = {...}) public interface DepartmentMapper extends GenericMapper<Department, DepartmentDTO> { @Mapping(target = "parent", ignore = true) @Mapping(target = "schedule", ignore = true) @Mapping(target = "language", ignore = true) @Named("toEntityLightweight") Department toEntityLightweight(@NonNull DepartmentDTO department); @IterableMapping(qualifiedByName = "toEntityLightweight") @Named("toEntitiesLightweight") List<Department> toEntitiesLightweight(@NonNull List<DepartmentDTO> departments); }
遇到的错误
首次构建错误
[ERROR] my-project/mapper/RoleMapper.java:[39,9] error: Ambiguous mapping methods found for mapping collection element to my-project.domain.Department: my-project.domain.Department my-project.mapper.DepartmentMapper.toEntity(my-project.dto.DepartmentDTO dto), my-project.domain.Department my-project.mapper.DepartmentMapper.toEntityLightweight(@lombok.NonNull my-project.dto.DepartmentDTO department).
修改qualifiedByName后的错误
将RoleMapper中的qualifiedByName改为"toEntityLightweight"后,出现重复的集合映射歧义错误:
[ERROR] my-project/mapper/RoleMapper.java:[37,4] error: Ambiguous mapping methods found for mapping property "java.util.List<my-project.dto.DepartmentDTO> departments" to @org.jetbrains.annotations.NotNull java.util.List<my-project.domain.Department>: @org.jetbrains.annotations.NotNull java.util.List<my-project.domain.Department> my-project.mapper.DepartmentMapper.toEntities(@lombok.NonNull java.util.List<my-project.dto.DepartmentDTO> dtos), java.util.List<my-project.domain.Department> my-project.mapper.DepartmentMapper.toEntitiesLightweight(@lombok.NonNull java.util.List<my-project.dto.DepartmentDTO> departments).
[ERROR] my-project/mapper/RoleMapper.java:[37,4] error: Ambiguous mapping methods found for mapping property "java.util.List<my-project.dto.DepartmentDTO> departments" to java.util.List<my-project.domain.Department>: @org.jetbrains.annotations.NotNull java.util.List<my-project.domain.Department> my-project.mapper.DepartmentMapper.toEntities(@lombok.NonNull java.util.List<my-project.dto.DepartmentDTO> dtos), java.util.List<my-project.domain.Department> my-project.mapper.DepartmentMapper.toEntitiesLightweight(@lombok.NonNull java.util.List<my-project.dto.DepartmentDTO> departments).
解决方案
1. 明确集合与元素映射的绑定关系
问题根源是MapStruct处理集合映射时,即便指定了集合级别的qualifiedByName,仍会检查单个元素的映射方法是否存在歧义。你的DepartmentMapper中已经通过@IterableMapping(qualifiedByName = "toEntityLightweight")为toEntitiesLightweight绑定了元素映射方法,这部分是正确的,只需确保RoleMapper的配置让MapStruct优先使用该集合方法,而非自动推导元素映射。
保持RoleMapper的原有配置不变即可:
@Mapper(uses = {DepartmentMapper.class}) public interface RoleMapper extends GenericMapper<Role, RoleDTO> { @Mapping(target = "departments", qualifiedByName = "toEntitiesLightweight") @Named("toEntityLightweight") Role toEntityLightweight(@NonNull RoleDTO role); }
2. 消除集合方法的歧义
如果仍报错,可在DepartmentMapper中为继承自GenericMapper的toEntities方法添加@Named注解,让两个集合方法的标识完全区分:
public interface GenericMapper<E extends AbstractEntity, D extends AbstractDTO> { @IterableMapping(qualifiedByName = "toEntity") @Named("toEntities") // 已有该注解,确保继承后仍有效 @NotNull List<E> toEntities(@NonNull List<D> dtos); // ... 其他代码 }
3. 重复错误的解释
两次错误是因为MapStruct分别检查了带@NotNull注解和不带注解的List<Department>返回类型约束,本质是同一个歧义问题,解决集合映射的歧义后即可同时消除这两条错误。
内容的提问来源于stack exchange,提问作者Aarne Avialaynen

