Search code examples
hibernatelazy-loadinghibernate-envers

hibernate envers and ordered lists


I've been using Hibernate Envers 3.6 quite happily for some time. However I've come across an issue with ordered lists which seems to cause Envers to fail.

We have a class A with a List of B, mapped one-to-many in e.g., in class A

  @OneToMany(mappedBy = "A", cascade = CascadeType.ALL, orphanRemoval = true)
public List<B> getB() {
    return this.B;
}

When I retrieve a revision, I load up the appropriate instance of A, then explicitly initialize the collection of Bs: E.g.,

for (B b: rvision.getB()){
   b.getClass();// init  fields
}

Recently though we needed to add an 'orderColumn' annotation on the getB() method to ensure consistent ordering of the list in the DB.

E.g.,

@OneToMany(mappedBy = "A", cascade = CascadeType.ALL, orphanRemoval = true)
 @OrderColumn(name="columnIndex")
    public List<B> getB() {
        return this.B;
    }

This completely breaks the collection initialization in Envers, failing with a null pointer exception:

java.lang.NullPointerException
at org.hibernate.envers.entities.mapper.relation.lazy.initializor.ListCollectionInitializor.addToCollection(ListCollectionInitializor.java:81)
at org.hibernate.envers.entities.mapper.relation.lazy.initializor.ListCollectionInitializor.addToCollection(ListCollectionInitializor.java:39)
at org.hibernate.envers.entities.mapper.relation.lazy.initializor.AbstractCollectionInitializor.initialize(AbstractCollectionInitializor.java:67)
at org.hibernate.envers.entities.mapper.relation.lazy.proxy.CollectionProxy.checkInit(CollectionProxy.java:50)
at org.hibernate.envers.entities.mapper.relation.lazy.proxy.CollectionProxy.size(CollectionProxy.java:55)
etc....

We have isolated this into a unit test; toggling the OrderColumn annotation is sufficient to cause this problem. The OrderColumn is working fine with regular Hibernate.

The problem seems to stem from line 47 of org.hibernate.envers.entities.mapper.relation.component.MiddleSimpleComponentMapper:

 return ((Map<String, Object>) data.get(verEntCfg.getOriginalIdPropName())).get(propertyName);

Basically the 'propertyName' it's looking for is called 'mapKey' and the lookup returns null when it shouldn't. This isn't a property of my objects so it must be some internal Envers property.

If anyone have a clue what is going on, I'd be very grateful! Thanks Richard


Solution

  • We ran into this issue with Hibernate Envers 4.x. The solution for us was to use the @AuditMappedBy annotation to identify the order column for Envers. As shown in the example here it is necessary to map the order column value into a field on the referenced entity to make it all work.