In my use case a have a Job and this Job has comments. Since you cannot edit comments I do not want to Audit this Entity. Here is code:
@Audited(withModifiedFlag = true)
@Entity
@Table(name = "JOB")
public class Job {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
@Column(name = "id")
private Integer id;
@Audited(targetAuditMode = RelationTargetAuditMode.NOT_AUDITED)
@OneToMany(cascade = CascadeType.ALL, orphanRemoval = true)
@JoinColumn(name = "j_id")
private List<Comment> comments;
}
@Entity
@Table(name = "COMMENT")
public class Comment {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
@Column(name = "id")
private Integer id;
@Column(name = "title")
private String title;
@Column(name = "text")
private String text;
}
Now I want to be able to fetch all Modifications related to comments
.
Of course Hibernate do not generate an COMMENT_AUD table. But it generates a JOB_COMMENT_AUD table.
How can I get the respective Comment entities from the AuditReader
? I am only able to read Job entities with the AuditReader. Or in other words: I need to have access to the JOB_COMMENT_AUD entries with the AuditReader.
Somehow it must be possible to get the respective Comment entity with the AuditReader. Why else should hibernate audit the relation into the auto-generated JOB_COMMENT_AUD table?
First it is important to understand what the RelationalTargetAuditMode.NOT_AUDITED
does.
This annotation will make Envers generate a join-table that maintains the foreign key to primary key relationship between the audited entity Job
and the ORM data table for Comment
entities.
In other words, Envers does not duplicate any of the columns in the audit schema for the Comment
entity but instead captures just the primary key so that when an audited Job
instance is populated, it knows how to properly set the collection of Comment
s.
From an AuditReader
, only Job
types can be queried. This is because you've marked only the Job
type to be audited and not the Comment
type. But that does not mean you cannot fetch the associated Comment
when you get an audited instance of a Job
.
final AuditReader auditReader = AuditReaderFactory.get( entityManager );
// Get the first revision of Job with id jobId
// This Job has 2 comments, so here we can get them easily.
final Job job = auditReader.find( Job.class, jobId, 1 );
assertEquals( 2, job.getComments().size() );
assertEquals( "Hello World", job.getComments().get( 0 ).getText() );
assertEquals( "Goodbye World", job.getComments().get( 1 ).getText() );
What Envers does is wrap your Comment
collection with a proxy so that when your code first attempts to access it, it checks to see if that collection has been initialized. If it hasn't, it will populate the collection based on a specialized query that knows how to take the revision data about the current Job
entity, the Job_Comment_AUD
table and the base Comment
table for your entity and load the entity data.
In other words, the proxy initialization fires a query like this for you:
SELECT c.*
FROM Comment c,
Job_Comment_AUD jc
WHERE jc.job_id = :jobId
AND jc.revision = :jobRevision
AND jc.comment_id = c.id
The beauty is your code doesn't need to execute any specific fetch or query using the Hibernate Session
or the JPA EntityManager
. Envers handles this seamlessly for you when you invoke the job.getComments()
collection.