Mapstruct双向映射使用add*方法报错的问题咨询
实体定义
@Entity, Getter, Setter public class Plant { // 子类 private String name; @ManyToOne(fetch = FetchType.LAZY) private PlantGroup plantGroup; } @Entity, Getter, Setter public class PlantGroup { private String name; @Setter(AccessLevel.NONE) @OneToMany(mappedBy = "plantGroup") private List<Plant> plants = new ArrayList<>(); public void addPlant(Plant plant) { plants.add(plant); plant.setPlantGroup(this); } }
DTO定义
@Getter @Setter public class PlantGroupDTO { private String name; private List<Long> plantIds; }
Mapper实现
@Mapper(componentModel = "spring", collectionMappingStrategy = CollectionMappingStrategy.ADDER_PREFERRED) public abstract class PlantGroupMapper { @Autowired PlantService plantService; public abstract PlantGroup map(PlantGroupDTO dto); public List<Plant> map(List<Long> ids) { // 期望这个方法被调用 } }
报错信息
Can't map property "List
plants" to "Plant plants". Consider to declare/implement a mapping method: "Plant map(List value)".
需求
将List<Long> plantIds映射为List<Plant> plants时,不仅通过服务获取Plant实例,还要通过plantGroup.addPlant(plant);维护双向关联,本质上要执行以下逻辑:
dto.getPlantIds() .forEach(plantId -> plantGroup.addPlant(plantService.findById(plantId) .orElseThrow(() -> new EntityNotFoundException(plantId, Plant.class))));
报错原因
MapStruct默认会对集合做逐个元素映射:它需要把DTO中plantIds列表的单个Long(ID),映射为实体plants列表的单个Plant对象。你定义的map(List<Long> ids)是处理整个列表的方法,但MapStruct找不到单个Long转Plant的映射逻辑,因此误判为要把整个List<Long>转换成单个Plant,从而抛出错误。
另外,虽然你设置了CollectionMappingStrategy.ADDER_PREFERRED(优先使用adder方法),但前提是MapStruct能正确完成单个元素的类型转换,否则无法触发addPlant方法的调用。
解决步骤
1. 定义单个ID转Plant的映射方法
补充单个Long到Plant的转换逻辑,让MapStruct能正确处理集合中的每一个元素:
public Plant map(Long plantId) { return plantService.findById(plantId) .orElseThrow(() -> new EntityNotFoundException(plantId, Plant.class)); }
2. 保留CollectionMappingStrategy配置
你已经设置了CollectionMappingStrategy.ADDER_PREFERRED,MapStruct会自动识别PlantGroup中的addPlant方法,用它来添加元素(而非直接给plants列表赋值,刚好匹配你给plants加的@Setter(AccessLevel.NONE)限制)。
3. 完整Mapper代码
@Mapper(componentModel = "spring", collectionMappingStrategy = CollectionMappingStrategy.ADDER_PREFERRED) public abstract class PlantGroupMapper { @Autowired PlantService plantService; public abstract PlantGroup map(PlantGroupDTO dto); // 单个ID转Plant的方法,MapStruct会自动用它处理集合元素 public Plant map(Long plantId) { return plantService.findById(plantId) .orElseThrow(() -> new EntityNotFoundException(plantId, Plant.class)); } }
4. 生成代码验证
MapStruct会自动生成符合你需求的逻辑,核心代码如下:
@Override public PlantGroup map(PlantGroupDTO dto) { if ( dto == null ) { return null; } PlantGroup plantGroup = new PlantGroup(); plantGroup.setName( dto.getName() ); if ( dto.getPlantIds() != null ) { for ( Long plantId : dto.getPlantIds() ) { plantGroup.addPlant( map( plantId ) ); } } return plantGroup; }
这段代码会循环处理每个plantId,通过服务获取对应Plant实例,再调用addPlant维护双向关联,完全匹配你期望的逻辑。
内容的提问来源于stack exchange,提问作者ch1ll

