Spring Boot 3.3.3迁移后出现Hibernate ModelPart空指针异常
Spring Boot 2.6.5迁移至3.x后Hibernate启动NPE问题
问题背景
将应用从Spring Boot 2.6.5迁移到3.3.3版本,通过OpenRewrite完成迁移后,本地启动时抛出NullPointerException,提示无法调用org.hibernate.metamodel.mapping.ModelPart.getNavigableRole(),因为subPart为null。排查时曾收到映射异常,称com.foo.mysvc.customerdata.model.CustomerAccount实体不存在id属性,尝试将主键字段custAccId改为id后问题依旧。
错误日志
:: Spring Boot :: (v3.3.3) 2024-09-04 02:03:48 [main] INFO com.foo.mysvc.Application - Starting Application using Java 17.0.6 with PID 91321 (/Users/johndoe/Projects/Marketplace/mysvc/target/classes started by johndoe in /Users/johndoe/Projects/Marketplace/mysvc) 2024-09-04 02:03:48 [main] DEBUG com.foo.mysvc.Application - Running with Spring Boot v3.3.3, Spring v6.1.12 2024-09-04 02:03:48 [main] INFO com.foo.mysvc.Application - No active profile set, falling back to 1 default profile: "default" 2024-09-04 02:03:49 [main] INFO o.s.d.r.c.RepositoryConfigurationDelegate - Bootstrapping Spring Data JPA repositories in DEFAULT mode. 2024-09-04 02:03:49 [main] INFO o.s.d.r.c.RepositoryConfigurationDelegate - Finished Spring Data repository scanning in 268 ms. Found 18 JPA repository interfaces. 2024-09-04 02:03:50 [main] INFO o.s.b.w.e.tomcat.TomcatWebServer - Tomcat initialized with port 8080 (http) 2024-09-04 02:03:50 [main] INFO o.a.coyote.http11.Http11NioProtocol - Initializing ProtocolHandler ["http-nio-8080"] 2024-09-04 02:03:50 [main] INFO o.a.catalina.core.StandardService - Starting service [Tomcat] 2024-09-04 02:03:50 [main] INFO o.a.catalina.core.StandardEngine - Starting Servlet engine: [Apache Tomcat/10.1.28] 2024-09-04 02:03:50 [main] INFO o.a.c.c.C.[Tomcat].[localhost].[/] - Initializing Spring embedded WebApplicationContext 2024-09-04 02:03:50 [main] INFO o.s.b.w.s.c.ServletWebServerApplicationContext - Root WebApplicationContext: initialization completed in 2083 ms 2024-09-04 02:03:50 [main] INFO com.zaxxer.hikari.HikariDataSource - HikariPool-1 - Starting... 2024-09-04 02:03:51 [main] INFO com.zaxxer.hikari.pool.HikariPool - HikariPool-1 - Added connection conn0: url=jdbc:h2:mem:mydb user=SA 2024-09-04 02:03:51 [main] INFO com.zaxxer.hikari.HikariDataSource - HikariPool-1 - Start completed. 2024-09-04 02:03:51 [main] INFO o.h.jpa.internal.util.LogHelper - HHH000204: Processing PersistenceUnitInfo [name: default] 2024-09-04 02:03:51 [main] INFO org.hibernate.Version - HHH000412: Hibernate ORM core version 6.5.2.Final 2024-09-04 02:03:51 [main] INFO o.h.c.i.RegionFactoryInitiator - HHH000026: Second-level cache disabled 2024-09-04 02:03:51 [main] INFO o.s.o.j.p.SpringPersistenceUnitInfo - No LoadTimeWeaver setup: ignoring JPA class transformer 2024-09-04 02:03:53 [main] INFO o.h.e.t.j.p.i.JtaPlatformInitiator - HHH000489: No JTA platform available (set 'hibernate.transaction.jta.platform' to enable JTA platform integration) 2024-09-04 02:03:53 [main] ERROR o.s.o.j.LocalContainerEntityManagerFactoryBean - Failed to initialize JPA EntityManagerFactory: [PersistenceUnit: default] Unable to build Hibernate SessionFactory; nested exception is java.lang.NullPointerException: Cannot invoke "org.hibernate.metamodel.mapping.ModelPart.getNavigableRole()" because "subPart" is null 2024-09-04 02:03:53 [main] WARN o.s.b.w.s.c.AnnotationConfigServletWebServerApplicationContext - Exception encountered during context initialization - cancelling refresh attempt: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'entityManagerFactory' defined in class path resource [org/springframework/boot/autoconfigure/orm/jpa/HibernateJpaConfiguration.class]: [PersistenceUnit: default] Unable to build Hibernate SessionFactory; nested exception is java.lang.NullPointerException: Cannot invoke "org.hibernate.metamodel.mapping.ModelPart.getNavigableRole()" because "subPart" is null 2024-09-04 02:03:53 [main] INFO com.zaxxer.hikari.HikariDataSource - HikariPool-1 - Shutdown initiated... 2024-09-04 02:03:53 [main] INFO com.zaxxer.hikari.HikariDataSource - HikariPool-1 - Shutdown completed. 2024-09-04 02:03:53 [main] INFO o.a.catalina.core.StandardService - Stopping service [Tomcat] 2024-09-04 02:03:53 [main] INFO o.s.b.a.l.ConditionEvaluationReportLogger - Error starting ApplicationContext. To display the condition evaluation report re-run your application with 'debug' enabled. 2024-09-04 02:03:53 [main] ERROR o.s.boot.SpringApplication - Application run failed org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'entityManagerFactory' defined in class path resource [org/springframework/boot/autoconfigure/orm/jpa/HibernateJpaConfiguration.class]: [PersistenceUnit: default] Unable to build Hibernate SessionFactory; nested exception is java.lang.NullPointerException: Cannot invoke "org.hibernate.metamodel.mapping.ModelPart.getNavigableRole()" because "subPart" is null at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1806) at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:600) at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:522) at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:337) at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234) at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:335) at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:205) at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:954) at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:625) at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:146) at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:754) at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:456) at org.springframework.boot.SpringApplication.run(SpringApplication.java:335) at org.springframework.boot.SpringApplication.run(SpringApplication.java:1363) at org.springframework.boot.SpringApplication.run(SpringApplication.java:1352) at com.foo.mysvc.Application.main(Application.java:19) Caused by: jakarta.persistence.PersistenceException: [PersistenceUnit: default] Unable to build Hibernate SessionFactory; nested exception is java.lang.NullPointerException: Cannot invoke "org.hibernate.metamodel.mapping.ModelPart.getNavigableRole()" because "subPart" is null at org.springframework.orm.jpa.AbstractEntityManagerFactoryBean.buildNativeEntityManagerFactory(AbstractEntityManagerFactoryBean.java:421) at org.springframework.orm.jpa.AbstractEntityManagerFactoryBean.afterPropertiesSet(AbstractEntityManagerFactoryBean.java:396) at org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean.afterPropertiesSet(LocalContainerEntityManagerFactoryBean.java:366) at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1853) at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1802) ... 15 common frames omitted Caused by: java.lang.NullPointerException: Cannot invoke "org.hibernate.metamodel.mapping.ModelPart.getNavigableRole()" because "subPart" is null at org.hibernate.boot.query.SqlResultSetMappingDescriptor$AttributeFetchDescriptor.resolve(SqlResultSetMappingDescriptor.java:373) at org.hibernate.boot.query.SqlResultSetMappingDescriptor$EntityResultDescriptor.lambda$resolve$0(SqlResultSetMappingDescriptor.java:264) at java.base/java.util.HashMap.forEach(HashMap.java:1421) at org.hibernate.boot.query.SqlResultSetMappingDescriptor$EntityResultDescriptor.resolve(SqlResultSetMappingDescriptor.java:261) at org.hibernate.boot.query.SqlResultSetMappingDescriptor.lambda$resolve$0(SqlResultSetMappingDescriptor.java:123) at java.base/java.util.ArrayList.forEach(ArrayList.java:1511) at org.hibernate.boot.query.SqlResultSetMappingDescriptor.resolve(SqlResultSetMappingDescriptor.java:122) at org.hibernate.query.internal.NamedObjectRepositoryImpl.lambda$prepare$3(NamedObjectRepositoryImpl.java:199) at java.base/java.util.HashMap$Values.forEach(HashMap.java:1065) at org.hibernate.boot.internal.MetadataImpl.visitNamedResultSetMappingDefinition(MetadataImpl.java:279) at org.hibernate.query.internal.NamedObjectRepositoryImpl.prepare(NamedObjectRepositoryImpl.java:197) at org.hibernate.boot.internal.SessionFactoryObserverForNamedQueryValidation.sessionFactoryCreated(SessionFactoryObserverForNamedQueryValidation.java:37) at org.hibernate.internal.SessionFactoryObserverChain.sessionFactoryCreated(SessionFactoryObserverChain.java:35) at org.hibernate.internal.SessionFactoryImpl.<init>(SessionFactoryImpl.java:322) at org.hibernate.boot.internal.SessionFactoryBuilderImpl.build(SessionFactoryBuilderImpl.java:457) at org.hibernate.jpa.boot.internal.EntityManagerFactoryBuilderImpl.build(EntityManagerFactoryBuilderImpl.java:1506) at org.springframework.orm.jpa.vendor.SpringHibernateJpaPersistenceProvider.createContainerEntityManagerFactory(SpringHibernateJpaPersistenceProvider.java:75) at org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean.createNativeEntityManagerFactory(LocalContainerEntityManagerFactoryBean.java:390) at org.springframework.orm.jpa.AbstractEntityManagerFactoryBean.buildNativeEntityManagerFactory(AbstractEntityManagerFactoryBean.java:409) ... 19 common frames omitted
实体类代码
package com.foo.mysvc.customerdata.model; import lombok.Data; import lombok.EqualsAndHashCode; import lombok.ToString; import org.hibernate.annotations.CreationTimestamp; import org.hibernate.annotations.UpdateTimestamp; import jakarta.persistence.*; import java.time.LocalDateTime; @Entity @Table(name="CUSTOMER_ACCOUNT") @Data public class CustomerAccount { @Id @Column(name="CUST_ACC_ID") private String custAccId; //pk @Column(name = "CUSTOMER_INFO_ID") private Long customerInfoId; @ToString.Exclude @EqualsAndHashCode.Exclude @OneToOne(fetch = FetchType.LAZY) @JoinColumn(name = "CUSTOMER_INFO_ID", referencedColumnName = "id", insertable=false, updatable=false) private CustomerInfo customerInfo; //fk @Column(name="INTERNAL_ACC_NUMBER") private String internalAccNumber; @Column(name="MASKED_NUMBER") private String maskedNumber; @Column(name="PRODUCT_DESCRIPTION") private String productDesc; @Column(name="ACC_TYPE") private String accountType; @Column(name="BASE_CCY") private String baseCurrency; @Column(name="ACTIVE") private boolean active; @CreationTimestamp private LocalDateTime createdAt; @UpdateTimestamp private LocalDateTime updatedAt; }
解决方案
从错误栈分析,问题和实体类主键无关——你的实体主键配置完全合法,改名为id属于无效操作。核心原因是Hibernate启动时验证**SQL结果集映射(SqlResultSetMapping)**时,发现了无效的属性引用,导致解析过程中抛出空指针。
具体排查和修复步骤:
全局搜索项目中的
@SqlResultSetMapping和@NamedNativeQuery- 检查所有结果集映射配置,核对
@FieldResult、@ColumnResult中指定的属性名是否和实体类实际字段完全匹配,注意大小写和拼写。 - 重点排查涉及
CustomerAccount或关联实体CustomerInfo的映射配置,看是否存在引用不存在属性的情况。
- 检查所有结果集映射配置,核对
检查OpenRewrite迁移后的注解变更
- 迁移过程中工具会将
javax.persistence替换为jakarta.persistence,但可能遗漏部分配置。确认所有JPA相关注解的包路径正确,特别是原生查询和结果集映射的注解。
- 迁移过程中工具会将
验证关联实体
CustomerInfo的配置- 确认
CustomerInfo实体的主键id存在且标注@Id注解,检查是否有结果集映射错误引用了该实体的属性。
- 确认
临时绕过验证(仅用于排查)
- 如果暂时无法定位问题,可添加以下配置关闭Hibernate的命名查询验证,先让应用启动再逐步排查:
此配置仅作为临时方案,最终必须修复无效的结果集映射配置。hibernate.session_factory.observers.enabled=false
- 如果暂时无法定位问题,可添加以下配置关闭Hibernate的命名查询验证,先让应用启动再逐步排查:
总结
该错误的根源是无效的SqlResultSetMapping配置,和实体主键无关。通过排查所有结果集映射和原生查询的属性引用,即可解决启动时的空指针问题。
内容的提问来源于stack exchange,提问作者user3762913
相关产品推荐
相关产品推荐

