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

SpringBoot3迁移Hibernate6时Envers JOINED继承递归关联NPE问题

Hibernate Envers 6.x JOINED继承+@Audited触发NullPointerException问题分析

问题现象

迁移Spring Boot应用至Spring Boot 3(对应Hibernate 6)后,启动时抛出NullPointerException:

Caused by: java.lang.NullPointerException: Cannot invoke "java.util.Map.get(Object)" because the return value of "java.util.Map.get(Object)" is null
at org.hibernate.envers.configuration.internal.metadata.AuditMetadataGenerator.addJoins(AuditMetadataGenerator.java:206)
at org.hibernate.envers.configuration.internal.metadata.AuditMetadataGenerator.generateSecondPass(AuditMetadataGenerator.java:409)
at org.hibernate.envers.configuration.internal.EntitiesConfigurator.configure(EntitiesConfigurator.java:86)
at org.hibernate.envers.boot.internal.EnversServiceImpl.initialize(EnversServiceImpl.java:129)
at org.hibernate.envers.boot.internal.AdditionalJaxbMappingProducerImpl.produceAdditionalMappings(AdditionalJaxbMappingProducerImpl.java:92)
at org.hibernate.boot.model.process.spi.MetadataBuildingProcess.complete(MetadataBuildingProcess.java:329)
at org.hibernate.jpa.boot.internal.EntityManagerFactoryBuilderImpl.metadata(EntityManagerFactoryBuilderImpl.java:1350)
at org.hibernate.jpa.boot.internal.EntityManagerFactoryBuilderImpl.build(EntityManagerFactoryBuilderImpl.java:1421)
at org.springframework.orm.jpa.vendor.SpringHibernateJpaPersistenceProvider.createContainerEntityManagerFactory(SpringHibernateJpaPersistenceProvider.java:66)
at org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean.createNativeEntityManagerFactory(LocalContainerEntityManagerFactoryBean.java:376)
at org.springframework.orm.jpa.AbstractEntityManagerFactoryBean.buildNativeEntityManagerFactory(AbstractEntityManagerFactoryBean.java:409)
at org.springframework.orm.jpa.AbstractEntityManagerFactoryBean.afterPropertiesSet(AbstractEntityManagerFactoryBean.java:396)
at org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean.afterPropertiesSet(LocalContainerEntityManagerFactoryBean.java:352)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1797)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1747)
... 110 common frames omitted

涉及的实体采用JOINED继承策略且标注了@Audited:

@Entity
@Table(name = "a")
@Audited
@DiscriminatorValue("DISCRIMINATOR")
public class A extends B {
   //...
}

@Entity
@Table(name = "b")
@Inheritance(strategy = InheritanceType.JOINED)
@Audited
public abstract class B {
   //...
}

问题结论

这确实是Hibernate Envers 6.x的Bug,该场景在Hibernate 5.6中可正常运行。

问题根源

Envers的AuditMetadataGenerator类处理实体关联时存在逻辑漏洞:

  • 第一遍处理实体时,通过entity.isJoinAware()判断是否将实体加入entityJoins集合,但JoinedSubclassPersistentEntity未重写该方法(父类PersistentEntity默认返回false),导致JOINED继承的子类实体未被加入集合
  • 第二遍处理关联时,仍会尝试访问该实体的关联信息,调用entityJoins.get(entityName)返回null,直接触发NullPointerException

兼容性说明

JOINED继承策略与@Audited理论上是兼容的,只是当前Envers版本在包含@NotAudited递归关联的特定场景下存在Bug,并非所有JOINED继承+@Audited的场景都会出错。

临时解决方案

  • 优先升级到Hibernate Envers的最新稳定版本,官方通常会在后续小版本中修复这类配置逻辑漏洞
  • 若无法升级,可临时调整实体结构:
    • 移除递归关联上的@NotAudited注解(或调整关联的审计配置)
    • 临时改用SINGLE_TABLE继承策略作为过渡方案
  • 复杂场景下可自定义Envers的MetadataGenerator扩展来绕过该问题,但该方式复杂度较高,不推荐作为长期方案

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.08 19:05:21