Setting the revision date manually with Hibernate

2019-08-24 02:32发布

问题:

As far as I know, Hibernate Envers stores a revision when you create, change or delete an object annotated with @Audited.

Envers sets automatically the revision date to the current time. Is it possibile to set this time manually?

I'd need this to handle a temporal collection where data has valid time, which I'd need to set manually.

回答1:

You can, but it may not seem intuitive at first.

When Envers creates its revision-entity instance, several things happen.

  1. The @RevisionTimestamp annotated property is set with the current time.
  2. The optional RevisionListener is called and supplied the revision-entity instance.

You can specify a RevisionListener in two ways and this really depends on whether or not your currently supplying a custom revision-entity instance or using the instance Envers resolves based on your setup.

Supplying Custom Revision Entity

In this situation, you can specifying your RevisionListener by setting it on the @RevisionEntity class annotation on the entity class.

@RevisionEntity(YourCustomRevisionListener.class)
public class CustomRevisionEntity {
  ...
}

Supplying RevisionListener via configuration

In this situation, you'll want add an additional bootstrap configuration property for Hibernate, either via your hibernate.properties file or in your code where you explicitly set the hibernate configuration properties:

org.hibernate.envers.revision_listener=com.company.envers.YourCustomRevisionListener

Regardless of which approach you take, you'll then implement the listener's contract and explicitly set the timestamp value based on whatever rules your application needs:

public class YourCustomRevisionListener implements RevisionListener {
  @Override
  public void newRevision(Object revisionEntity) {
    // I am going to assume here you're using a custom revision entity.
    // If you are not, you'll need to cast it to the appropriate class implementation.
    final CustomRevisionEntity revisionEntityImpl = (CustomRevisionEntity) revisionEntity;
    revisionEntityImpl.setTimestamp( resolveValidTimestampValue() );
  }

  private long resolveValidTimestampValue() {
    // implement your logic here.
  }
}

There are a couple caveats here. If you need to resolve the value from some bean in your application space, you'll need to determine which of the following applies to you:

Using Hibernate Envers version prior to 5.3

In this case you'll have to use the legacy approach of ThreadLocal variables to pass application scope instances/values to access those inside the listener.

Using Hibernate Envers 5.3 or later with CDI

In this case you can simply inject the CDI bean using CDI's injection since we added support to automatically resolve CDI beans when we create the listener instance.

Using Hibernate Envers 5.3 or later with Spring 5.1+

You can inject spring beans directly into the listener using Spring's injection annotations just like the listener were a spring-bean.

Using Hibernate Envers 5.3 or later with Spring prior to 5.1

In this case, you'll need to use the legacy approach of ThreadLocal variables since Spring Framework didn't add support for injecting beans into Hibernate beans until 5.1.