如何用MapStruct将List<String>与单个Integer映射为实体列表?
解决MapStruct多元素列表与单个参数映射实体集合的报错问题
需求说明
我有一个包含多个字符串的List,还有一个作为数据库外键的Integer值(示例值为1),需要把二者结合:为List里的每个字符串创建一个SurveyAnswerEntity,每个实体的question.questionId都使用这个Integer值,比如:
- 字符串"oneString" → Entity1,question.questionId=1
- 字符串"twoString" → Entity2,question.questionId=1
- 字符串"threeString" → Entity3,question.questionId=1
现有代码
SurveyAnswerEntity.java
public class SurveyAnswerEntity extends BaseEntity { @GeneratedValue(strategy = GenerationType.IDENTITY) @Id private int answerId; @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "question_id") private SurveyQuestionEntity question; private String answer; }
SurveyContentModel.java
public class SurveyContentModel { private String question; private List<String> answerList; }
SurveyAnswerMapper.java
public interface SurveyAnswerMapper { SurveyAnswerMapper MAPPER = Mappers.getMapper(SurveyAnswerMapper.class); @Mapping(target = "question.questionId", source = "questionId") SurveyAnswerEntity toEntity(String answer, Integer questionId); List<SurveyAnswerEntity> toEntityList(List<String> answer, Integer questionId); }
报错信息
运行时出现错误:
error: The return type List is an abstract class or interface. Provide a non abstract / non interface result type or a factory method.
移除questionId参数后代码能正常运行,推测是单个字段无法直接映射到List集合导致的。
报错原因
MapStruct处理集合映射的默认逻辑是只识别集合类型的源参数,当传入单个Integer参数+List参数时,它没法自动把这个Integer参数传递给每个元素的toEntity方法,因此抛出了返回类型相关的错误。
解决方案
方案一:手动遍历调用单个映射方法(最直接)
不需要修改原有的toEntity方法,直接在业务代码里循环处理:
List<String> answers = Arrays.asList("oneString", "twoString", "threeString"); Integer questionId = 1; List<SurveyAnswerEntity> entities = answers.stream() .map(answer -> SurveyAnswerMapper.MAPPER.toEntity(answer, questionId)) .collect(Collectors.toList());
这种方式简单直观,不需要额外修改MapStruct配置,适合快速解决问题。
方案二:用@Context注解传递单个参数
修改SurveyAnswerMapper,用@Context标注要传递的单个参数,再通过@IterableMapping指定元素的映射方法:
public interface SurveyAnswerMapper { SurveyAnswerMapper MAPPER = Mappers.getMapper(SurveyAnswerMapper.class); @Mapping(target = "question.questionId", source = "questionId") SurveyAnswerEntity toEntity(String answer, @Context Integer questionId); @IterableMapping(elementTargetType = SurveyAnswerEntity.class) List<SurveyAnswerEntity> toEntityList(List<String> answers, @Context Integer questionId); }
@Context注解用于传递映射过程中需要的上下文参数,MapStruct会自动把这个参数传递给每个元素的toEntity方法。
方案三:封装参数到包装类
创建一个包装类,把List和questionId封装在一起,让MapStruct可以识别:
// 新建参数包装类 public class AnswerListWrapper { private List<String> answerList; private Integer questionId; // 构造方法、getter、setter public AnswerListWrapper(List<String> answerList, Integer questionId) { this.answerList = answerList; this.questionId = questionId; } // 省略getter和setter }
然后修改Mapper:
public interface SurveyAnswerMapper { SurveyAnswerMapper MAPPER = Mappers.getMapper(SurveyAnswerMapper.class); @Mapping(target = "question.questionId", source = "wrapper.questionId") @Mapping(target = "answer", source = "answer") SurveyAnswerEntity toEntity(String answer, @Context AnswerListWrapper wrapper); List<SurveyAnswerEntity> toEntityList(AnswerListWrapper wrapper); }
使用时:
List<String> answers = Arrays.asList("oneString", "twoString", "threeString"); AnswerListWrapper wrapper = new AnswerListWrapper(answers, 1); List<SurveyAnswerEntity> entities = SurveyAnswerMapper.MAPPER.toEntityList(wrapper);
这种方式适合参数较多的场景,封装后代码结构更清晰。
内容的提问来源于stack exchange,提问作者남혁준
相关产品推荐
相关产品推荐

