我尝试将SpringBoot应用程序迁移到SpringBoot 3。SpringBoot 3使用Hibernate 6。我的应用程序由于以下错误而拒绝启动
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
在深入研究Envers代码后,发现问题位于org.hibernate.envers.configuration.internal.metadata.AuditMetadaGenerator类中。
在第337行,有一个条件允许在first pass
的envers期间引用一个审计类。
if ( entity.isJoinAware() ) {
final JoinAwarePersistentEntity joinAwareEntity = (JoinAwarePersistentEntity) entity;
createJoins( persistentClass, joinAwareEntity, auditingData );
addJoins( persistentClass, propertyMapper, auditingData, persistentClass.getEntityName(), mappingData, true );
}
private void createJoins(PersistentClass persistentClass, JoinAwarePersistentEntity entity, ClassAuditingData auditingData) {
final Iterator<org.hibernate.mapping.Join> joins = persistentClass.getJoinIterator();
final Map<org.hibernate.mapping.Join, Join> joinElements = new HashMap<>();
entityJoins.put( persistentClass.getEntityName(), joinElements );
....
这是在second pass
行206期间被调用的列表。
while ( joins.hasNext() ) {
final org.hibernate.mapping.Join join = joins.next();
final Join entityJoin = entityJoins.get( entityName ).get( join );
这里entityJoins.get(entityName)
返回我的一个实体的null
。
此实体已正确标注@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 {
//...
}
在我的理解中,使用InheritanceType.JOINED
的值指定Inheritance会使envers创建一个JoinedSubclassPersistentEntity,该JoinedSubclassPersistentEntity本身继承自PersistentEntity。
这个PersistentEntity
有一个方法:
public boolean isJoinAware() {
return false;
}
它会被它的子进程(如RootPersistentEntity)覆盖,方法是:
@Override
public boolean isJoinAware() {
return true;
}
JoinedSubclassPersistentEntity
不是使用Joined继承策略时生成的类,它不会进行此覆盖。
这导致我的实体没有被添加到first pass
,但仍然由second pass
处理。
所以问题是?这是Envers的一个bug吗?我可以在@Audited类中使用Joined继承策略吗?
它在hib5.6中运行良好。
[编辑]:我设法用一个简单的测试类重现了这个错误:
@Entity
@Audited
@DiscriminatorValue("OPTION")
public class Child extends Parent{
private String propA;
@OneToMany(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
@JoinTable(name = "recursive_children_child", joinColumns = {@JoinColumn(name = "child_id", nullable = false, updatable = false)}, inverseJoinColumns = {@JoinColumn(name = "recursive_id", nullable = false, updatable = false)})
@NotAudited
@OrderBy("propA DESC")
private List<Child> children = new ArrayList<>();
@ManyToOne(fetch = FetchType.LAZY)
@JoinTable(name = "recursive_children_child", joinColumns = {@JoinColumn(name = "recursive_id")}, inverseJoinColumns = {@JoinColumn(name = "child_id")})
@NotAudited
private Child recursiveChild;
public Child(String propA) {
this.propA = propA;
}
public Child() {
}
public String getPropA() {
return propA;
}
public void setPropA(String propA) {
this.propA = propA;
}
}
问题似乎与Child
类中的递归关系有关。尽管存在@Audited
注解,但Ember仍尝试审计关系。
链接到重现错误的项目=〉https://github.com/scandinave/envers6-migration-bug
1条答案
按热度按时间6qfn3psc1#
通过阅读Jakarta持久性规范,我终于发现了这个问题。
JoinTable注解用于实体关联的Map。JoinTable注解在关联的拥有方指定
在我的项目中,关联的两边都有一个
@JoinTable
注解,这在Hibernate 6之前是有效的,但现在不行了,我只需要删除不需要的@JoinTable就可以解决这个错误。