将附加数据传递给 Hibernate Envers 中的 Custom RevisionEntity 的方法? [英] Ways to pass additional data to Custom RevisionEntity in Hibernate Envers?

查看:22
本文介绍了将附加数据传递给 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( Class revisionEntityClass, boolean persist ) 方法来获取当前修订,并用所需的信息.

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

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

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 安全或者创建你自己的持有者,可能在自定义用户详细信息对象中.用于保存侦听器所需信息的上下文对象.

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.

值得注意的是,其他 API getCurrentRevision(Session,boolean) 在 Hibernate 5.2 中已被弃用,并计划在 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天全站免登陆