Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Hibernate Envers - include date of when change happened

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

like image 557
Magick Avatar asked Feb 16 '16 13:02

Magick


2 Answers

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?

like image 148
Ricardo Vila Avatar answered Sep 28 '22 03:09

Ricardo Vila


There is a table created automatically by envers named: REVINFO that contains the timestamp.

It contains REV as a key that is the number of your revision in your "_AUD" table. This number is unique between all the "_AUD" tables.

See paragraph: 15.8. Understanding the Envers Schema in https://docs.jboss.org/hibernate/orm/4.0/devguide/en-US/html/ch15.html

like image 23
Othman Doghri Avatar answered Sep 28 '22 03:09

Othman Doghri