如何在Hibernate Envers中将其他数据传递到Custom RevisionEntity? [英] Ways to pass additional data to Custom RevisionEntity in Hibernate Envers?

查看:113
本文介绍了如何在Hibernate Envers中将其他数据传递到Custom RevisionEntity?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

这是RESTful网络应用程序.我正在使用Hibernate Envers来存储历史数据.除了修订号和时间戳,我还需要存储其他详细信息(例如:IP地址和经过身份验证的用户). Envers 提供了多种方法来进行自定义修订真棒的实体.我在修订实体上设置自定义数据时遇到问题.

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. 
    }
}

由于newRevision()方法不允许使用任何其他参数,因此我无法将自定义数据(如用户名和ip)传递给它.我该怎么办?

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还提供了另一种方法,例如:

Envers also provides another approach as:

使用org.hibernate.envers.RevisionListener的另一种方法是改为调用org.hibernate.envers.AuditReader接口的getCurrentRevision(ClassversionEntityClass,booleanpersist)方法来获取当前版本,并用它填充所需的信息.

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:

更改我当前的dao方法,例如:

Change my current dao method like:

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

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);

}

我对这种方法不太满意,因为保留审核数据对我来说似乎是一个贯穿各领域的问题.并且我通过HTTP请求对象获得了UserName和Ip等数据.因此,所有这些数据都需要从应用程序(控制器)的入口点一直流到最低层(Dao层).

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).

还有其他方法可以实现这一目标吗?我正在使用Spring.

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

我正在想象像Spring这样的东西,它保留有关特定方法调用所属的堆栈"的信息.这样,在调用newRevision()时,我知道在入口点哪个特定的调用会导致该调用.而且,我还能以某种方式获取传递给调用堆栈的第一个方法的参数.

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.

推荐答案

一种好的方法是利用ThreadLocal变量.

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

作为示例,Spring Security具有一个过滤器,该过滤器初始化存储在SecurityContextHolder中的线程局部变量,然后您可以通过执行以下操作从该特定线程访问此数据:

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();

因此,请设想一下您的Web框架调用的其他拦截器,该拦截器可以向spring安全性上下文中添加其他信息,如果使用spring security则可以在自定义用户详细信息对象中添加,或者创建自己的所有者&上下文对象,用于保存侦听器所需的信息.

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.

然后它变得很简单:

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.

值得注意的是,从Hibernate 5.2开始不推荐使用其他API getCurrentRevision(Session,boolean),并计划在6.0中将其删除.尽管可以引入一种替代方法,但是执行这种类型的逻辑的预期方式是使用RevisionListener.

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.

这篇关于如何在Hibernate Envers中将其他数据传递到Custom RevisionEntity?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆