You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

ModelMapper转换Set<Author>到Set<Integer>时出现空指针错误求助

问题分析

从报错栈信息来看,空指针异常(NPE)出现在map(Autor::getId)执行阶段,说明Livro实体的关联集合中存在null的Autor/Genero对象,调用getId()时触发了空指针。另外Hibernate返回的PersistentSet虽然实现了Set接口,但也可能因泛型匹配细节导致转换异常。

修复方案

方案1:在转换器中过滤null元素

修改转换器逻辑,在流式处理时先过滤掉集合中的null实体,同时额外过滤可能的null ID,避免空指针:

import java.util.Objects;
// ... 其他导入

@Configuration
public class MapperConfig {

    @Bean
    public ModelMapper modelMapper(){
        ModelMapper modelMapper = new ModelMapper();

        Converter<Set<Autor>, Set<Integer>> autoresParaAutoresId = context -> {
            Set<Autor> source = context.getSource();
            if (source == null) {
                return null;
            }
            return source.stream()
                    .filter(Objects::nonNull) // 过滤null的Autor对象
                    .map(Autor::getId)
                    .filter(Objects::nonNull) // 过滤null的ID值
                    .collect(Collectors.toSet());
        };

        Converter<Set<Genero>, Set<Integer>> generosParaGenerosId = context -> {
            Set<Genero> source = context.getSource();
            if (source == null){
                return null;
            }
            return source.stream()
                    .filter(Objects::nonNull)
                    .map(Genero::getId)
                    .filter(Objects::nonNull)
                    .collect(Collectors.toSet());
        };
        
        modelMapper.typeMap(Livro.class , LivroCriadoDto.class).addMappings(
                src -> {
                    src.using(autoresParaAutoresId).map(Livro::getAutores, LivroCriadoDto::setAutores);
                    src.using(generosParaGenerosId).map(Livro::getGeneros, LivroCriadoDto::setGeneros);
                });

        return modelMapper;
    }
}

方案2:从源头避免集合中出现null元素

在Livro实体中添加关联对象的校验逻辑,确保不会将null对象加入集合:

public class Livro {
    // ... 其他字段和方法

    public void addAutor(Autor autor) {
        if (autor != null) {
            this.autores.add(autor);
            // 如果Autor实体也维护双向关联,同步更新
            autor.getLivros().add(this);
        }
    }

    public void addGenero(Genero genero) {
        if (genero != null) {
            this.generos.add(genero);
            genero.getLivros().add(this);
        }
    }
}

方案3:处理Hibernate懒加载问题(若存在)

如果是因为关联集合懒加载、转换时Session已关闭导致无法访问元素,需在Service层确保集合初始化:

  • 使用JPQL的fetch join查询时直接加载关联集合:
@Query("SELECT l FROM Livro l JOIN FETCH l.autores JOIN FETCH l.generos WHERE l.id = :id")
Livro findByIdWithAssociations(@Param("id") Integer id);
  • 或手动触发集合初始化:
import org.hibernate.Hibernate;
// ...

Livro livro = livroRepository.findById(id).orElseThrow();
Hibernate.initialize(livro.getAutores());
Hibernate.initialize(livro.getGeneros());

方案4:优化转换器的类型兼容性

将转换器的源类型改为Collection,兼容Hibernate的PersistentSet及其他集合实现:

Converter<Collection<Autor>, Set<Integer>> autoresParaAutoresId = context -> {
    Collection<Autor> source = context.getSource();
    if (source == null) {
        return null;
    }
    return source.stream()
            .filter(Objects::nonNull)
            .map(Autor::getId)
            .filter(Objects::nonNull)
            .collect(Collectors.toSet());
};
验证修复

修改后重新执行转换代码,确认空指针异常消失,且LivroCriadoDto中的autores和generos集合正确填充了对应实体的ID。

内容的提问来源于stack exchange,提问作者Natasha Mota

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.03 22:20:44