Ways to pass additional data to Custom RevisionEntity in Hibernate Envers?

China☆狼群 提交于 2020-02-02 03:46:22

问题


It's RESTful web app. I am using Hibernate Envers to store historical data. Along with revision number and timestamp, I also need to store other details (for example: IP address and authenticated user). Envers provides multiple ways to have a custom revision entity which is awesome. I am facing problem in setting the custom data on the revision entity.

@RevisionEntity( MyCustomRevisionListener.class )
public class MyCustomRevisionEntity extends DefaultRevisionEntity {
    private String userName;

    private String ip;

    //Accessors
}

public class MyCustomRevisionListener implements RevisionListener {
    public void newRevision( Object revisionEntity ) {
        MyCustomRevisionEntity customRevisionEntity = ( MyCustomRevisionEntity ) revisionEntity;
        //Here I need userName and Ip address passed as arguments somehow, so that I can set them on the revision entity. 
    }
}

Since newRevision() method does not allow any additional arguments, I can not pass my custom data (like username and ip) to it. How can I do that?

Envers also provides another approach as:

An alternative method to using the org.hibernate.envers.RevisionListener is to instead call the getCurrentRevision( Class revisionEntityClass, boolean persist ) method of the org.hibernate.envers.AuditReader interface to obtain the current revision, and fill it with desired information.

So using the above approach, I'll have to do something like this:

Change my current dao method like:

public void persist(SomeEntity entity) {
     ...
     entityManager.persist(entity);
     ...
}

to

public void persist(SomeEntity entity, String userName, String ip) {
     ...
     //Do the intended work
     entityManager.persist(entity);
     //Do the additional work
     AuditReader reader = AuditReaderFactory.get(entityManager)
     MyCustomRevisionEntity revision = reader.getCurrentRevision(MyCustomRevisionEntity, false);
     revision.setUserName(userName);
     revision.setIp(ip);

}

I don't feel very comfortable with this approach as keeping audit data seems a cross cutting concern to me. And I obtain the userName and Ip and other data through HTTP request object. So all that data will need to flow down right from entry point of application (controller) to the lowest layer (dao layer).

Is there any other way in which I can achieve this? I am using Spring.

I am imagining something like Spring keeping information about the 'stack' to which a particular method invocation belongs. So that when newRevision() in invoked, I know which particular invocation at the entry point lead to this invocation. And also, I can somehow obtain the arguments passed to first method of the call stack.


回答1:


One good way to do this would be to leverage a ThreadLocal variable.

As an example, Spring Security has a filter that initializes a thread local variable stored in SecurityContextHolder and then you can access this data from that specific thread simply by doing something like:

SecurityContext ctx = SecurityContextHolder.getSecurityContext();
Authorization authorization = ctx.getAuthorization();

So imagine an additional interceptor that your web framework calls that either adds additional information to the spring security context, perhaps in a custom user details object if using spring security or create your own holder & context object to hold the information the listener needs.

Then it becomes a simple:

public class MyRevisionEntityListener implements RevisionListener {
  @Override
  public void newRevision(Object revisionEntity) {
    // If you use spring security, you could use SpringSecurityContextHolder.
    final UserContext userContext = UserContextHolder.getUserContext();
    MyRevisionEntity mre = MyRevisionEntity.class.cast( revisionEntity );
    mre.setIpAddress( userContext.getIpAddress() );
    mre.setUserName( userContext.getUserName() );
  } 
}

This feels like the cleanest approach to me.

It is worth noting that the other API getCurrentRevision(Session,boolean) was deprecated as of Hibernate 5.2 and is scheduled for removal in 6.0. While an alternative means may be introduced, the intended way to perform this type of logic is using a RevisionListener.



来源:https://stackoverflow.com/questions/37815460/ways-to-pass-additional-data-to-custom-revisionentity-in-hibernate-envers

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!