Spring Boot 2.5迁移至3.1.5时Hibernate 6.2报'targetModelPart'为空异常
问题原因分析:Spring Boot 3.1.5(Hibernate 6.2)迁移中的"targetModelPart"空指针异常
问题背景
将遗留应用从Spring Boot 2.5升级到3.1.5后,Hibernate 6.2抛出包含"targetModelPart" is null的空指针异常,关联的一对多映射代码如下:
@JoinColumns({ @JoinColumn(name = "TICKER_SERIAL", referencedColumnName = "TICKER_SERIAL", insertable = false, updatable = false), }) @OneToMany(fetch = FetchType.EAGER, cascade = {CascadeType.PERSIST, CascadeType.MERGE, CascadeType.REFRESH, CascadeType.DETACH}) private Set<FundTickerDescription> fundTickerDescriptions; @JsonProperty("INDEX_BENCHMARK") @Fetch(FetchMode.JOIN) @JoinColumn(name = "TICKER_SERIAL", referencedColumnName = "TICKER_SERIAL") @OneToMany(fetch = FetchType.EAGER, cascade = {CascadeType.PERSIST, CascadeType.MERGE, CascadeType.REFRESH, CascadeType.DETACH}) private List<FundBenchmarkV2> fundBenchmarkV2s;
异常堆栈核心报错信息:
Suppressed: java.lang.NullPointerException: Cannot invoke "org.hibernate.metamodel.mapping.PropertyBasedMapping.getPropertyAccess()" because "targetModelPart" is null at org.hibernate.metamodel.mapping.internal.SimpleForeignKeyDescriptor.<init>(SimpleForeignKeyDescriptor.java:130) at org.hibernate.metamodel.mapping.internal.MappingModelCreationHelper.interpretPluralAttributeMappingKeyDescriptor(MappingModelCreationHelper.java:784)
问题根源
你提到关联实体TickerDescriptionsV2的ID类被错误标记为@Embeddable,而实际应该是用于@IdClass的普通类,这正是触发异常的核心原因:
- Hibernate 6.x注解校验更严格:Spring Boot 2.5对应的Hibernate 5.x版本对这类注解错误的容忍度较高,可能不会触发明显异常;但Hibernate 6.2在构建映射元数据时,会严格校验主键类的注解类型,不允许混淆使用。
- 错误注解导致映射元数据混乱:当ID类被标记为
@Embeddable,Hibernate会将其识别为可嵌入组件,而非实体的主键类。在处理一对多关联的外键映射时,无法正确解析目标实体的主键结构,导致targetModelPart无法被初始化,最终触发空指针。 - 关联映射的连锁影响:异常中涉及的多个一对多关联(如
fundBenchmarkV2s、fundTickerDescriptions)都依赖关联实体的主键映射正确性,一旦某个实体的主键注解错误,会连锁影响所有关联的映射初始化流程。
解决方案
修正TickerDescriptionsV2的ID类注解,并调整实体类配置:
- 移除ID类上的
@Embeddable注解,将其改为普通Java类,确保包含与实体主键字段对应的属性,并重写equals()和hashCode()方法。 - 在
TickerDescriptionsV2实体类上使用@IdClass注解指定该ID类。
示例代码:
// 修正后的ID类(移除@Embeddable) public class TickerDescriptionsV2Id implements Serializable { private Long tickerSerial; // 其他主键字段... // 实现equals()和hashCode()方法 @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; TickerDescriptionsV2Id that = (TickerDescriptionsV2Id) o; return Objects.equals(tickerSerial, that.tickerSerial); } @Override public int hashCode() { return Objects.hash(tickerSerial); } } // 修正后的实体类 @Entity @IdClass(TickerDescriptionsV2Id.class) public class TickerDescriptionsV2 { @Id private Long tickerSerial; // 其他字段及关联映射... }
内容的提问来源于stack exchange,提问作者ahrooran
相关产品推荐
相关产品推荐

