We have just started using Hibernate Envers, and it works well for logging what changed, however, is there a way that it can also log when the change happened?
So, can it add a datetime column to the audit table?
According to the Envers documentation, this should happen by default:
When Envers starts a new revision, it creates a new revision entity which stores information about the revision. By default, that includes just
- revision number - An integral value (int/Integer or long/Long). Essentially the primary key of the revision
- revision timestamp - either a long/Long or java.util.Date value representing the instant at which the revision was made. When using a java.util.Date, instead of a long/Long for the revision timestamp, take care not to store it to a column data type which will loose precision.
So, my understanding is, that there is no required actions needed to get the revision timestamp. However, in my case, there is no revision timestamp in the tables created by envers.
Thanks
You need to define a custom RevisionEntity to be used by Envers so you can add the attributes you need. It is necessary to annotate the class you pretend to use as custom revision entity:
@RevisionEntity(AuditingRevisionListener.class)
In this entity you can define what you need. For example, this should be a good starting point:
@Entity
@Table(name = "DATA_REVIEW_TABLE")
@RevisionEntity(AuditingRevisionListener.class)
public class AuditedRevisionEntity {
@RevisionNumber
@Id
@SequenceGenerator(name = "revisionSeq", sequenceName = "REVISION_DATOS_ID_SEQ", allocationSize = 1, initialValue = 1)
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "revisionSeq")
private int id;
@RevisionTimestamp
private Date modifiedAt;
private String username;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
@Temporal(value = TemporalType.TIMESTAMP)
public Date getModifiedAt() {
return modifiedAt;
}
public void setModifiedAt(Date modifiedAt) {
this.modifiedAt = modifiedAt;
}
}
Also you must define a Revision listener to handle how data is initialized in your custom revision entity. Your listener must implement RevisionListener.
public class AuditingRevisionListener implements RevisionListener {
private static Log log = LogFactory.getLog(AuditingRevisionListener.class.getName());
@Override
public void newRevision(Object revisionEntity) {
AuditedRevisionEntity revEntity = (AuditedRevisionEntity) revisionEntity;
String userName = null;
try {
if (SecurityContextHolder.getContext().getAuthentication() != null) {
Object principal = getCurrentUserInfo();
if (principal == null) {
userName = "system";
} else if (principal instanceof User) {
userName = ((User) principal).getUsername();
}
}
} catch (Exception e) {
log.error("Error auditing username.", e);
}
revEntity.setUsername(userName);
revEntity.setModifiedAt(new Date());
}
}
Have in mind that those classes must be accesible by hibernate, check this: Why Hibernate Envers is ignoring my custom RevisionEntity?