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

Spring Boot实体类NullPointerException问题排查与解决求助

问题:JPA多主键实体查询时抛出NullPointerException

实体类代码

Evolution 实体

@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
@Entity
@Table(name = "evolutions")
@IdClass(value = EvolutionPkey.class)
public class Evolution {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "pokemon_id")
    private int pokemonId;

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "form_id")
    private int formId;

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "next_pokemon_id")
    private int nextPokemonId;

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "next_form_id")
    private int nextFormId;

    @Column(nullable = false)
    private int stage;

    @ManyToOne
    @JoinColumn(name = "pokemon_id", referencedColumnName = "pokemon_id")
    private Pokemon pokemon;
}

EvolutionPkey 主键类

@Data
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
public class EvolutionPkey implements Serializable{
    public int pokemonId;
    public int formId;
    public int nextPokemonId;
    public int nextFormId;
}

Pokemon 实体

public class Pokemon {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "pokemon_id")
    private int pokemonId;

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "form_id")
    private int formId;

    // 其他字段省略

    @OneToMany(mappedBy = "pokemon", cascade=CascadeType.ALL)
    private List<Evolution> evolutions;
}

Repository与查询代码

PokemonRepository 接口

public interface PokemonRepository extends JpaRepository<Pokemon, PokemonPkey>, JpaSpecificationExecutor<Pokemon> {
    public Optional<List<Pokemon>> findByPokemonId(int pokemonId);
}

服务类查询代码

@Autowired
private PokemonRepository pokemonRepository;
pokemonRepository.findByPokemonId(pokemonId)

报错信息

Resolved [java.lang.NullPointerException: Cannot invoke "org.hibernate.metamodel.mapping.AttributeMapping.getStateArrayPosition()" because the return value of "org.hibernate.persister.entity.AbstractEntityPersister.findAttributeMapping(String)" is null]

修复思路

  • 修正复合主键的自增配置:Evolution的四个@Id字段全部标注@GeneratedValue(strategy = GenerationType.IDENTITY)完全不合理,数据库无法同时让四个主键字段自增,这会导致Hibernate无法正确映射主键生成逻辑。需要根据业务逻辑调整:仅保留实际由数据库自增的字段(通常复合主键中不会多个自增),或者去掉不需要自增的主键字段的@GeneratedValue注解。
  • 调整主键类的字段访问权限:EvolutionPkey中的字段使用public修饰,不符合JPA规范,应该改为private,并通过@Data注解提供getter/setter(@Data已经包含这些,所以只需把public改成private),确保Hibernate能通过反射正确访问主键字段。
  • 补全Pokemon实体的必要注解:Pokemon类缺少@Entity、@Table注解,同时作为复合主键实体,需要添加@IdClass(PokemonPkey.class)(需创建对应的PokemonPkey主键类),以及@Getter/@Setter或@Data注解,否则JPA无法识别该类为实体类,导致映射错误。
  • 修复关联映射的重复列问题:Evolution中pokemonId既是主键字段,又是@ManyToOne关联的外键列,重复映射会导致Hibernate混淆。应该在@ManyToOne注解上添加@MapsId("pokemonId"),让JPA将关联的Pokemon的主键值映射到Evolution的pokemonId主键字段,避免重复映射同一列:
    @ManyToOne
    @MapsId("pokemonId")
    @JoinColumn(name = "pokemon_id", referencedColumnName = "pokemon_id")
    private Pokemon pokemon;
    
  • 修正Repository方法的返回值:findByPokemonId方法返回Optional<List<Pokemon>>是错误的,Optional用于包装单个对象,集合类型应该直接返回List<Pokemon>(空集合时返回空列表而非null),正确写法:
    List<Pokemon> findByPokemonId(int pokemonId);
    

内容的提问来源于stack exchange,提问作者Hiroto Nakashima

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.27 21:24:54