如何用ModelMapper将实体关联列表扁平化为ID列表?
解决ModelMapper将JPA实体扁平化映射的问题
你的问题出在自定义PropertyMap的实现方式上——ModelMapper在初始化PropertyMap的时候,source是一个代理占位对象,并不是实际的ResourceItem实例,这时候直接调用source.getResourceCategories()会返回null,进而触发NullPointerException导致映射配置失败。
下面给你两种正确的实现方式,都能避免这个问题:
方法一:使用Converter处理列表转换
先定义一个转换器,专门把List<ResourceCategory>转换成List<Integer>(提取ID集合),这个转换器会在实际映射时才执行,此时source是真实的实体对象:
@Bean public ModelMapper modelMapper() { ModelMapper mapper = new ModelMapper(); // 设置严格匹配策略,避免自动映射时出现意外的字段匹配 mapper.getConfiguration().setMatchingStrategy(MatchingStrategies.STRICT); // 创建转换器:将ResourceCategory列表转为ID列表 Converter<List<ResourceCategory>, List<Integer>> categoryIdConverter = context -> { List<ResourceCategory> sourceList = context.getSource(); if (sourceList == null || sourceList.isEmpty()) { return Collections.emptyList(); } return sourceList.stream() .map(ResourceCategory::getID) .collect(Collectors.toList()); }; // 配置PropertyMap,使用转换器完成映射 PropertyMap<ResourceItem, ResourceItemDTO> itemMap = new PropertyMap<>() { @Override protected void configure() { // 使用转换器映射resourceCategories到resourceCategoriesID using(categoryIdConverter).map(source.getResourceCategories()).setResourceCategoriesID(null); // 手动映射file的path到filePath(如果自动映射不生效的话) map(source.getFile().getPath()).setFilePath(null); } }; mapper.addMappings(itemMap); return mapper; }
方法二:使用Lambda风格的TypeMap配置(更简洁)
ModelMapper支持用Lambda表达式直接定义字段映射逻辑,这种方式不需要创建单独的Converter,代码更紧凑,同样能避免初始化时的NPE:
@Bean public ModelMapper modelMapper() { ModelMapper mapper = new ModelMapper(); mapper.getConfiguration().setMatchingStrategy(MatchingStrategies.STRICT); // 配置ResourceItem到ResourceItemDTO的映射 mapper.typeMap(ResourceItem.class, ResourceItemDTO.class) // 映射resourceCategories到resourceCategoriesID .addMappings(m -> m.map( src -> { if (src.getResourceCategories() == null) { return Collections.emptyList(); } return src.getResourceCategories().stream() .map(ResourceCategory::getID) .collect(Collectors.toList()); }, ResourceItemDTO::setResourceCategoriesID )) // 映射FileResource的path到filePath .addMappings(m -> m.map(src -> src.getFile().getPath(), ResourceItemDTO::setFilePath)); // 如果需要配置Tag到TagDTO的自动映射,ModelMapper通常能自动处理(字段名匹配的话) // 如果字段名不匹配,可以在这里补充Tag的TypeMap配置 return mapper; }
关键注意点
- 处理Null值:一定要判断
resourceCategories是否为null,避免stream()时触发NPE,返回空列表而不是null更符合DTO的设计规范。 - 匹配策略:设置
MatchingStrategies.STRICT可以让ModelMapper只映射字段名完全匹配的属性,减少意外的错误映射。 - 其他字段映射:比如
filePath是从FileResource的path字段提取的,需要手动指定映射关系,因为字段名不直接匹配(file.pathvsfilePath)。
这样修改后,你的测试代码应该就能正常运行,正确将ResourceCategory列表扁平化为ID集合了。
内容的提问来源于stack exchange,提问作者KellyM
相关产品推荐
相关产品推荐

