0

I'm using Hibernate with Envers, 4.3.6 FINAL. I have set up auditing using the annotations, and all of my audit tables have been created and are being populated correctly when I save information in my application. One of my audited entities has a mapping table to additional entities, with a @ManyToMany configuration.

Employer -> Designations

The employer can have 0 to n Designation entities associated with it. The mapping table is standard, just having two columns, a foreign key to the Employers table and a foreign key to the Designations table. Envers created an auditing table for this mapping table and records are appropriately recorded, with inserts or deletes entered with the same REV as the Employer when the Employer is edited.

What I'm trying to do now is retrieve the audit information from the database so I can present it for reporting purposes. Retrieving the list of revisions for my Employer is working and the state of the Employer for a given revision is accurate. What I haven't been able to figure out is how to retrieve the audits for the mapping table. I have no actual entity for these pairings. They are put together using the javax.persistence.JoinTable annotation. As such, there's no record for an entity in my REVCHANGES table (I'm setting org.hibernate.envers.track_entities_changed_in_revision to true). Looking at the database, there are records for my EmployerDesignations mapping table and the corresponding EmployerDesignations_AUD audit table, but I don't know how to retrieve these audit records nor how to even know when they have been changed for a given revision.

How can the audit history for a mapping table be retrieved?

Employer class:

@Entity
@DynamicInsert
@DynamicUpdate
@SelectBeforeUpdate
@Table(name="EMPLOYERS")
@Audited
public class Employer implements Serializable {

    //All other fields omitted for brevity
    ...

    private List<Designations> designations;
    @ManyToMany
    @JoinTable(name = "EMPLOYERDESIGNATIONS", 
        joinColumns =           { @JoinColumn(name = "FK_EMPLOYER", nullable = false) },
        inverseJoinColumns =    { @JoinColumn(name = "FK_DESIGNATION", nullable = false) })
    public List<Designations> getDesignations() {
        return designations;
    }
    public void setDesignations(List<Designations> designations) {
        this.designations= designations;
    }
}

Designation class:

@Entity
@DynamicInsert
@DynamicUpdate
@SelectBeforeUpdate
@Table(name="DESIGNATIONS")
@Audited
public class Designation implements Serializable {

    //All other fields omitted for brevity
    ...

    private List<Employer> employers;
    @ManyToMany
    @JoinTable(name = "EMPLOYERDESIGNATIONS", 
        joinColumns =           { @JoinColumn(name = "FK_DESIGNATION", nullable = false) },
        inverseJoinColumns =    { @JoinColumn(name = "FK_EMPLOYER", nullable = false) })
    public List<Employer> getEmployers() {
        return employers;
    }
    public void setEmployers(List<Employer> employers) {
        this.employers= employers;
    }
}

Test Code for audit data retrieval:

List<Number> revisions = reader.getRevisions(Employer.class, employerId);
System.out.println(revisions);
for (Number revisionNum : revisions) {
    Employer employer = reader.find(Employer.class, employerId, revisionNum);
    System.out.println(employer);
}
Curtis Snowden
  • 407
  • 1
  • 4
  • 20

1 Answers1

1

All you need to do is to retrieve the Employer instance and then simply access the getter for the association and whatever state that associated had when the entity was audited will be made available automatically.

If we assume the first operation in the Envers environment was you creating an Employer and associating it with two Designation entities via your join, then when you fetch the Employer instance for revision 1, the collection would have two elements.

Later on in transaction 2, you modify Employer and you remove one Designation and add a new Designation to the entity, then when you fetched the Employer at revision 2, it would still contain two Designation entity instances, but those instances would be the one that remained from the first revision and the second which you added as part of the second revision.

Naros
  • 19,928
  • 3
  • 41
  • 71
  • Is there something special that needs to be done for that? I'm afraid my designation list is empty even when I use the getter directly. Breakpointing my code, I see that the designation list is actually an instance of org.hibernate.envers.internal.entities.mapper.relation.lazy.proxy.ListProxy. – Curtis Snowden May 11 '17 at 19:11
  • Correct its a proxy which will be loaded when the list is first accessed in some meaningful way, e.g. getting its size or elements. – Naros May 11 '17 at 22:22
  • I must be doing something wrong at some level. I have tried initializing the list using size() as you stated and as described in [this question](http://stackoverflow.com/questions/5261139/hibernate-envers-initializing-envers-proxies) but the resulting list is empty. I know there is audit data for the given revision number I'm testing, but it seems that the designation list is not being populated at all. – Curtis Snowden May 11 '17 at 22:33
  • From what I can see code wise, this should work. Please see my gist at https://gist.github.com/Naros/96537efe7e2a3e5cabaebef623f2f77d for an example where I mocked your entities and it works fine. Understand what you could be facing is something broken in 4.3 and if thats true, you'll need to update to 5.2. – Naros May 12 '17 at 13:08
  • Thank you. I'll test that out with 4.3 and 5.2 to see if there's any difference between the two versions. – Curtis Snowden May 12 '17 at 14:18