javahibernatehibernate-enversnhibernate-enversaudit-tables

Hibernate Enver : @AuditJoinTable rows missing


Given an Entity that is audited by Envers, which contains one collection.

Entity A

  @Audited
    public class A{
     @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private int id;
    ....
    @OneToMany
    @JoinColumn(name = "a_id")
    @AuditJoinTable(name = "A_B_AUDIT"
            ,inverseJoinColumns = @JoinColumn(name = "a"))
    private List<B> bs;
    ....
}

Entity B

@Audited
public class B{
 @Id
 private int id;
 ....
 @Column(name = "a_id")
 private int aId;

 @ManyToOne
 @JoinColumn(name = "a_id", insertable = false, updatable = false)
 private A a;
}

As per their documentation Envers stores the audit information for additions and deletions to these AuditJoinTables (A_B_AUDIT). But Unfortunately, this is not working and the rows inside the tables are missing.

When I run my project following tables gets created :

A_AUDIT

B_AUDIT

A_B_AUDIT

I've separate controllers to persist A object and B Object. When I try to save B with aId and A, audit_table (A_B_AUDIT) does not gets updated but B_AUDIT with updated revision gets updated.

Can someone please let me know what i am missing here.

Thank you !!

Hibernate Enver Version : 5.1.4.Final


Solution

  • As said in the comments, your issue is that you're performing the persistence of these two entities in separate transactions but you aren't making the proper association and thus you're tainting the state of Hibernate's first level cache. Inadvertently, you're also causing Envers not to be able to properly audit your entities because you're not giving it all the proper state.

    In order for this use case to work with Envers, you need to modify how you are handling the persistence of your Entity B given that it is the only thing that seems to know about the relationship with Entity A.

    During the persistence of Entity B, you should likely do this:

    if ( b.getAId() != null ) {
      A a = entityManager.find( A.class, b.getAId() );
      a.getBs().add( b );
      a = entityManager.merge( a );
    
      b.setA( a );
      entityManager.persist( b );
    }
    

    This means during the persistence of Entity B, you should get an audit row added in the audit join table like you expected and the state in the Hibernate first level cache will contain all the proper references between the two entities.

    If we put Envers aside for a moment and focus on normal JPA provider usage, you should be able to do this after you persist B and it would work:

    final A theA = b.getA();
    assertNotNull( theA );
    assertTrue( !theA.getBs().isEmpty() );
    assertTrue( theA.getBs().contains( b ) );
    

    Obviously if you aren't setting the associations, these assertions (which should all pass) won't. The only time they would pass would be when you requery entity B, but that should not be necessary.

    Hopefully you understand.