I'm migrating from Hibernate Envers 5.x to 6. In my project there are a few entities with many-to-many relationships, the junction tables are represented with entities, having a few additional fields. In these entities both sides of the relationship are @ManyToOne fields.
@Audited(withModifiedFlag = true)
@Entity
public class Document {
@Id
@GeneratedValue
private Long id;
@Lob
private String title;
@OneToMany(mappedBy = "document", cascade = CascadeType.ALL, orphanRemoval = true, fetch = FetchType.EAGER)
@OrderBy("position")
private List<DocumentAuthorEmployee> authors;
...
@Audited(withModifiedFlag = true)
@Entity
public class DocumentAuthorEmployee {
@Id
@GeneratedValue
private Long id;
@ManyToOne
@JoinColumn(name = "document_id")
private Document document;
@ManyToOne(fetch = FetchType.EAGER)
@JoinColumn(name = "employee_id")
private Employee employee;
private int position;
private String note;
...
@Audited(withModifiedFlag = true)
@Entity
public class Employee {
@Id
@GeneratedValue
private Long id;
@Column(nullable = false, length = 1000)
private String name;
...
I have a custom RevisionListener which checks for the last two revisions of the modified entity and creates a diff from it. In this listener the AuditReader#find method throws a NullPointerException:
java.lang.NullPointerException: Cannot invoke "org.hibernate.envers.internal.entities.EntityConfiguration.getRelationDescription(String)" because "entCfg" is null
at org.hibernate.envers.internal.entities.EntitiesConfigurations.getRelationDescription(EntitiesConfigurations.java:101) ~[hibernate-envers-6.2.17.Final.jar:6.2.17.Final]
at org.hibernate.envers.internal.entities.mapper.relation.ToOneIdMapper.isIgnoreNotFound(ToOneIdMapper.java:226) ~[hibernate-envers-6.2.17.Final.jar:6.2.17.Final]
at org.hibernate.envers.internal.entities.mapper.relation.ToOneIdMapper.nullSafeMapToEntityFromMap(ToOneIdMapper.java:171) ~[hibernate-envers-6.2.17.Final.jar:6.2.17.Final]
at org.hibernate.envers.internal.entities.mapper.relation.ToOneIdMapper.nullSafeMapToEntityFromMap(ToOneIdMapper.java:152) ~[hibernate-envers-6.2.17.Final.jar:6.2.17.Final]
at org.hibernate.envers.internal.entities.mapper.relation.AbstractToOneMapper.mapToEntityFromMap(AbstractToOneMapper.java:57) ~[hibernate-envers-6.2.17.Final.jar:6.2.17.Final]
at org.hibernate.envers.internal.entities.mapper.MultiPropertyMapper.mapToEntityFromMap(MultiPropertyMapper.java:201) ~[hibernate-envers-6.2.17.Final.jar:6.2.17.Final]
at org.hibernate.envers.internal.entities.EntityInstantiator.createInstanceFromVersionsEntity(EntityInstantiator.java:90) ~[hibernate-envers-6.2.17.Final.jar:6.2.17.Final]
at org.hibernate.envers.internal.entities.EntityInstantiator.addInstancesFromVersionsEntities(EntityInstantiator.java:153) ~[hibernate-envers-6.2.17.Final.jar:6.2.17.Final]
at org.hibernate.envers.query.internal.impl.AbstractAuditQuery.applyProjections(AbstractAuditQuery.java:352) ~[hibernate-envers-6.2.17.Final.jar:6.2.17.Final]
at org.hibernate.envers.query.internal.impl.EntitiesAtRevisionQuery.list(EntitiesAtRevisionQuery.java:139) ~[hibernate-envers-6.2.17.Final.jar:6.2.17.Final]
at org.hibernate.envers.query.internal.impl.AbstractAuditQuery.getSingleResult(AbstractAuditQuery.java:119) ~[hibernate-envers-6.2.17.Final.jar:6.2.17.Final]
at org.hibernate.envers.internal.reader.AuditReaderImpl.find(AuditReaderImpl.java:122) ~[hibernate-envers-6.2.17.Final.jar:6.2.17.Final]
at org.hibernate.envers.internal.reader.AuditReaderImpl.find(AuditReaderImpl.java:95) ~[hibernate-envers-6.2.17.Final.jar:6.2.17.Final]
at org.hibernate.envers.internal.reader.AuditReaderImpl.find(AuditReaderImpl.java:89) ~[hibernate-envers-6.2.17.Final.jar:6.2.17.Final]
at com.github.csaadaam.envers.manytoone.npe.listener.RevisionListener.entityChanged(RevisionListener.java:20) ~[classes/:na]
at org.hibernate.envers.internal.revisioninfo.DefaultRevisionInfoGenerator.entityChanged(DefaultRevisionInfoGenerator.java:98) ~[hibernate-envers-6.2.17.Final.jar:6.2.17.Final]
at org.hibernate.envers.internal.synchronization.EntityChangeNotifier.entityChanged(EntityChangeNotifier.java:46) ~[hibernate-envers-6.2.17.Final.jar:6.2.17.Final]
at org.hibernate.envers.internal.synchronization.AuditProcess.executeInSession(AuditProcess.java:126) ~[hibernate-envers-6.2.17.Final.jar:6.2.17.Final]
at org.hibernate.envers.internal.synchronization.AuditProcess.doBeforeTransactionCompletion(AuditProcess.java:175) ~[hibernate-envers-6.2.17.Final.jar:6.2.17.Final]
at org.hibernate.envers.internal.synchronization.AuditProcessManager$1.doBeforeTransactionCompletion(AuditProcessManager.java:47) ~[hibernate-envers-6.2.17.Final.jar:6.2.17.Final]
at org.hibernate.engine.spi.ActionQueue$BeforeTransactionCompletionProcessQueue.beforeTransactionCompletion(ActionQueue.java:1012) ~[hibernate-core-6.2.17.Final.jar:6.2.17.Final]
... 42 common frames omitted
There is a change in the ToOneIdMapper class since hibernate 6.0 that might be causing the issue:
https://github.com/hibernate/hibernate-orm/commit/b384b37f395f14f5e64c6b6d4d00e0868d62fe01#diff-3435a0e26ab386be275368ac69b447f002655fba8802e9d6d9397d5176b6af17L161
In 5.x there used to be a check for whether the referenced entity is audited, and since it is, it was skipped in 5.x:
if ( !referencedEntity.isAudited())
Now it tries to get the referencingEntityName, but since the map contains no $type$, it is null and later in the EntitiesConfigurations#getRelationDescription it causes a NPE, because neither of the paths provide an EntityConfiguration here:
final EntityConfiguration entCfg;
if ( isVersioned( entityName ) ) {
entCfg = get( entityName );
}
else {
entCfg = getNotVersionEntityConfiguration( entityName );
}
So it throws a NPE here: https://github.com/hibernate/hibernate-orm/blob/6.2/hibernate-envers/src/main/java/org/hibernate/envers/internal/entities/EntitiesConfigurations.java#L101
I haven't found anything about it in the migration docs, am i missing something?
I've created a repo for showcasing my issue: https://github.com/csaadaam/envers-manytoone-npe
Any help would be greatly appreciated.