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
相关产品推荐
相关产品推荐

