0

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

Himanshu Arora
  • 688
  • 1
  • 9
  • 20
  • I've tested both with master and 5.1.4 and I cannot reproduce your issue so it must be something very specific with how you're setting the values in both sides of the relationship. Could you include your code where you're creating your entities and associating them prior to persisting? – Naros Dec 11 '17 at 13:40
  • Hi @Naros, I've separate controllers for both of them and on the basis of aId mapping (Hibernate) I'm picking the associations. – Himanshu Arora Dec 12 '17 at 07:38
  • It sounds as if you're persisting `B` and `A` in separate transactions and the only association you're building between them is setting `B#aId` with the identifier of `A`. If that is true and you're not also explicitly adding `B` to the list in `A` then you're going to end up not placing values in the join table. This isn't how persistence works. You need to make sure you're persisting all necessary attributes together or else you'll end up with partial state and Envers won't populate the audit history correctly. If this isn't what you're doing, then again you need to post the actual code. – Naros Dec 12 '17 at 13:40
  • Hi @Naros, yes, you're correct on the way of persisting data. One thing is clear that as per my implementation I cannot use AuditJoinTable as Envers does not work like this but Is there a way to achieve audit details apart of query every associations in such type of scenarios ? – Himanshu Arora Dec 13 '17 at 06:11

1 Answers1

1

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.

Naros
  • 19,928
  • 3
  • 41
  • 71