Hibernate.orgCommunity Documentation
Envers provides an easy way to log additional data for each revision. You simply need
to annotate one entity with @RevisionEntity
, and a new instance of
this entity will be persisted when a new revision is created (that is, whenever an
audited entity is modified). As revisions are global, you can have at most one revisions entity.
Please note that the revision entity must be a mapped Hibernate entity.
This entity must have at least two properties:
an integer- or long-valued property, annotated with @RevisionNumber
. Most
often, this will be an auto-generated primary key.
a long- or j.u.Date- valued property, annotated with @RevisionTimestamp
. Value of
this property will be automatically set by Envers.
You can either add these properties to your entity, or extend
org.hibernate.envers.DefaultRevisionEntity
, which already has those two properties.
When using a Date
, instead of a long/Long
for the revision timestamp,
take care not to use a mapping of the property which will loose precision (for example, using
@Temporal(DATE)
is wrong, as it doesn't store the time information, so many of your
revisions will appear to happen at exactly the same time). A good choice is a
@Temporal(TIMESTAMP)
.
To fill the entity with additional data, you'll need to implement the
org.jboss.envers.RevisionListener
interface. Its newRevision method will
be called when a new revision is created, before persisting the revision entity.
The implementation should be stateless and thread-safe. The listener then has to be
attached to the revisions entity by specifying it as a parameter to the
@RevisionEntity
annotation.
Alternatively, you can use the getCurrentRevision
method of the
AuditReader
interface to obtain the current revision, and fill it with desired information.
The method has a persist
parameter specifying, if the revision entity should be persisted
before returning. If set to true
, the revision number will be available in the returned
revision entity (as it is normally generated by the database), but the revision entity will be persisted
regardless of wheter there are any audited entities changed. If set to false
, the revision
number will be null
, but the revision entity will be persisted only if some audited entities
have changed.
A simplest example of a revisions entity, which with each revision associates the username of the user making the change is:
package org.jboss.envers.example; import org.hibernate.envers.RevisionEntity; import org.hibernate.envers.DefaultRevisionEntity; import javax.persistence.Entity; @Entity @RevisionEntity(ExampleListener.class) public class ExampleRevEntity extends DefaultRevisionEntity { private String username; public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } }
Or, if you don't want to extend any class:
package org.hibernate.envers.example; import org.hibernate.envers.RevisionNumber; import org.hibernate.envers.RevisionTimestamp; import org.hibernate.envers.RevisionEntity; import javax.persistence.Id; import javax.persistence.GeneratedValue; import javax.persistence.Entity; @Entity @RevisionEntity(ExampleListener.class) public class ExampleRevEntity { @Id @GeneratedValue @RevisionNumber private int id; @RevisionTimestamp private long timestamp; private String username; // Getters, setters, equals, hashCode ... }
An example listener, which, if used in a JBoss Seam application, stores the currently logged in user username:
package org.hibernate.envers.example; import org.hibernate.envers.RevisionListener; import org.jboss.seam.security.Identity; import org.jboss.seam.Component; public class ExampleListener implements RevisionListener { public void newRevision(Object revisionEntity) { ExampleRevEntity exampleRevEntity = (ExampleRevEntity) revisionEntity; Identity identity = (Identity) Component.getInstance("org.jboss.seam.security.identity"); exampleRevEntity.setUsername(identity.getUsername()); } }
Having an "empty" revision entity - that is, with no additional properties except the two mandatory ones - is also an easy way to change the names of the table and of the properties in the revisions table automatically generated by Envers.
In case there is no entity annotated with @RevisionEntity
, a default
table will be generated, with the name REVINFO
.
By default entity types that have been changed in each revision are not being tracked. This implies the
necessity to query all tables storing audited data in order to retrieve changes made during
specified revision. Users are allowed to implement custom mechanism of tracking modified entity names.
In this case, they shall pass their own implementation of
org.hibernate.envers.EntityTrackingRevisionListener
interface as the value
of @org.hibernate.envers.RevisionEntity
annotation.
EntityTrackingRevisionListener
interface exposes one method that notifies
whenever audited entity instance has been added, modified or removed within current revision boundaries.
Example 4.1. Custom implementation of tracking entity classes modified during revisions
CustomEntityTrackingRevisionListener.java
public class CustomEntityTrackingRevisionListener
implements EntityTrackingRevisionListener {
@Override
public void entityChanged(Class entityClass, String entityName,
Serializable entityId, RevisionType revisionType,
Object revisionEntity) {
String type = entityClass.getName();
((CustomTrackingRevisionEntity)revisionEntity).addModifiedEntityType(type);
}
@Override
public void newRevision(Object revisionEntity) {
}
}
CustomTrackingRevisionEntity.java
@Entity
@RevisionEntity(CustomEntityTrackingRevisionListener.class)
public class CustomTrackingRevisionEntity {
@Id
@GeneratedValue
@RevisionNumber
private int customId;
@RevisionTimestamp
private long customTimestamp;
@OneToMany(mappedBy="revision", cascade={CascadeType.PERSIST, CascadeType.REMOVE})
private Set<ModifiedEntityTypeEntity> modifiedEntityTypes =
new HashSet<ModifiedEntityTypeEntity>();
public void addModifiedEntityType(String entityClassName) {
modifiedEntityTypes.add(new ModifiedEntityTypeEntity(this, entityClassName));
}
...
}
ModifiedEntityTypeEntity.java
@Entity
public class ModifiedEntityTypeEntity {
@Id
@GeneratedValue
private Integer id;
@ManyToOne
private CustomTrackingRevisionEntity revision;
private String entityClassName;
...
}
CustomTrackingRevisionEntity revEntity = getAuditReader().findRevision(CustomTrackingRevisionEntity.class, revisionNumber); Set<ModifiedEntityTypeEntity> modifiedEntityTypes = revEntity.getModifiedEntityTypes()
Copyright © 2004 Red Hat Inc.