
在使用mapstruct的`@mappingtarget`注解更新对象属性时,开发者可能会遇到“can't generate mapping method from iterable type to non-iterable type.”错误。此错误通常发生在尝试将一个集合类型(如`list`)映射到目标对象的某个集合属性,但mapstruct误将其识别为将集合映射到整个目标对象本身。本文将深入解析此问题的原因,并提供一个实用的变通方案,通过引入一个额外参数来解决这一映射歧义。
在复杂的Java应用中,对象之间的属性映射是常见的操作。MapStruct作为一个强大的代码生成器,极大地简化了这一过程。然而,在使用@MappingTarget注解来更新现有对象而非创建新实例时,有时会遇到一些非直观的错误。
假设我们有以下数据结构:
// Class A
import java.util.List;
import lombok.Getter;
import lombok.Setter;
@Getter
@Setter
public class A {
    String nameA;
    List<C> namesC;
}
// Class B
import java.util.List;
import lombok.Getter;
import lombok.Setter;
@Getter
@Setter
public class B {
    String nameB;
    List<D> namesD;
}
// Class C
import lombok.Getter;
import lombok.Setter;
@Getter
@Setter
public class C {
    String nameC;
}
// Class D
import lombok.Getter;
import lombok.Setter;
@Getter
@Setter
public class D {
    String nameD;
}以及两个MapStruct映射器接口:
// CDMapper
import java.util.List;
import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
@Mapper(componentModel="spring")
public interface CDMapper {
    @Mapping(target="nameC", source="nameD")
    C DtoC(D d);
    List<C> DstoCs(List<D> ds);
}
// ABMapper
import java.util.List;
import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
import org.mapstruct.MappingTarget;
@Mapper(uses = {CDMapper.class})
public interface ABMapper {
    @Mapping(target="nameA", source="nameB")
    A BtoA(B b);
    @Mapping(target="namesC", source="ds")
    void fillList(@MappingTarget A a, List<D> ds); // 错误发生在此行
}在ABMapper的fillList方法上,我们遇到了编译错误:“Can't generate mapping method from iterable type to non-iterable type.”(无法从迭代类型生成映射方法到非迭代类型)。
这个错误令人困惑,因为我们明确使用了@Mapping(target="namesC", source="ds")来指示MapStruct将List<D> ds映射到目标对象A的namesC属性,而不是将整个List<D>映射到整个A对象。然而,MapStruct却报告了迭代类型List<D>与非迭代类型A之间的不兼容。
此问题的根源在于MapStruct在解析具有特定签名的映射方法时的内部机制。当一个映射方法包含且仅包含两个参数时——一个用@MappingTarget注解修饰的目标对象,以及一个源对象——MapStruct可能会默认将其解释为尝试将整个源对象映射到整个目标对象。
尽管我们通过@Mapping注解指定了ds应该映射到a.namesC,但在这种双参数方法签名下,MapStruct的预处理器在进行初步类型兼容性检查时,可能会优先检查源参数(List<D> ds)是否可以直接映射到目标参数(A a)。由于List<D>是迭代类型而A是非迭代类型,这种直接的整体映射是不兼容的,因此MapStruct抛出了“Can't generate mapping method from iterable type to non-iterable type.”的错误。
简而言之,MapStruct在处理void fillList(@MappingTarget A a, List<D> ds);时,误将List<D> ds视为要映射到整个A a对象,而非其内部的namesC属性。
解决此问题的一个有效变通方案是,在映射方法中引入一个额外的、不参与实际映射的参数。通过增加参数数量,我们可以改变MapStruct对方法签名的解析方式,使其不再将源参数误认为要映射到整个@MappingTarget对象。
修改后的ABMapper接口如下:
import java.util.List;
import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
import org.mapstruct.MappingTarget;
@Mapper(uses = {CDMapper.class})
public interface ABMapper {
    @Mapping(target="nameA", source="nameB")
    A BtoA(B b);
    // 方便外部调用的默认方法
    default void fillList(A a, List<D> ds) {
        // 调用实际的映射方法,传入一个额外的布尔值
        fillList(a, ds, false);
    }
    // 实际的映射方法,引入一个额外的布尔参数
    @Mapping(target="namesC", source="ds")
    void fillList(@MappingTarget A a, List<D> ds, boolean unused);
}在这个解决方案中:
通过引入第三个参数,MapStruct的解析器不再将List<D> ds误认为要映射到整个A a对象,而是正确地根据@Mapping(target="namesC", source="ds")注解的指示,将ds映射到A对象的namesC属性。此时,CDMapper的DstoCs方法将负责完成List<D>到List<C>的实际转换。
当使用MapStruct的@MappingTarget注解进行对象更新,并且遇到“Can't generate mapping method from iterable type to non-iterable type.”错误时,这通常是由于MapStruct在处理仅有两个参数的方法时,错误地将源集合类型视为要映射到整个目标对象。通过在映射方法中引入一个额外的、不参与实际映射的参数(并结合default方法来保持API简洁),可以有效改变MapStruct的解析行为,使其正确识别@Mapping注解指定的属性级映射。这是一个实用的变通方案,可帮助开发者在MapStruct的特定场景下顺利完成映射任务。
以上就是MapStruct @MappingTarget 迭代类型映射错误解决方案的详细内容,更多请关注php中文网其它相关文章!
                        
                        每个人都需要一台速度更快、更稳定的 PC。随着时间的推移,垃圾文件、旧注册表数据和不必要的后台进程会占用资源并降低性能。幸运的是,许多工具可以让 Windows 保持平稳运行。
                Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号