Java 使用Hibernate实体中的现有数据填充envers修订表

Java 使用Hibernate实体中的现有数据填充envers修订表,java,hibernate,hibernate-envers,Java,Hibernate,Hibernate Envers,我正在向现有的hibernate实体添加envers。就审计而言,一切都进展顺利,但是查询是另一个问题,因为修订表中没有填充现有数据。还有其他人已经解决了这个问题吗?也许您已经找到了用现有表填充修订表的方法?我只是想问一下,我相信其他人会觉得它很有用 看一看 基本上,您可以使用@RevisionEntity annotation定义自己的“修订类型”, 然后实现一个RevisionListener接口来插入额外的审计数据, 喜欢当前用户和高级操作。通常这些都是从ThreadLocal上下文中提取

我正在向现有的hibernate实体添加envers。就审计而言,一切都进展顺利,但是查询是另一个问题,因为修订表中没有填充现有数据。还有其他人已经解决了这个问题吗?也许您已经找到了用现有表填充修订表的方法?我只是想问一下,我相信其他人会觉得它很有用

看一看

基本上,您可以使用@RevisionEntity annotation定义自己的“修订类型”, 然后实现一个RevisionListener接口来插入额外的审计数据, 喜欢当前用户和高级操作。通常这些都是从ThreadLocal上下文中提取的。

您不需要这样做。
AuditQuery允许您通过以下方式获取修订实体和数据修订:

AuditQuery query = getAuditReader().createQuery()
                .forRevisionsOfEntity(YourAuditedEntity.class, false, false);

这将构造一个查询,返回对象[3]的列表。第一个元素是您的数据,第二个是修订实体,第三个是修订类型。

我们通过运行一系列原始SQL查询来填充初始数据,以模拟“插入”所有现有实体,就像它们刚刚在同一时间创建一样。例如:

insert into REVINFO(REV,REVTSTMP) values (1,1322687394907); 
-- this is the initial revision, with an arbitrary timestamp

insert into item_AUD(REV,REVTYPE,id,col1,col1) select 1,0,id,col1,col2 from item; 
-- this copies the relevant row data from the entity table to the audit table

请注意,REVTYPE值为0,表示插入(与修改相反)

我们解决了用现有数据填充审核日志的问题,如下所示:

SessionFactory defaultSessionFactory;

// special configured sessionfactory with envers audit listener + an interceptor 
// which flags all properties as dirty, even if they are not.
SessionFactory replicationSessionFactory;

// Entities must be retrieved with a different session factory, otherwise the 
// auditing tables are not updated. ( this might be because I did something 
// wrong, I don't know, but I know it works if you do it as described above. Feel
// free to improve )

FooDao fooDao = new FooDao();
fooDao.setSessionFactory( defaultSessionFactory );
List<Foo> all = fooDao.findAll();

// cleanup and close connection for fooDao here.
..

// Obtain a session from the replicationSessionFactory here eg.
Session session = replicationSessionFactory.getCurrentSession();

// replicate all data, overwrite data if en entry for that id already exists
// the trick is to let both session factories point to the SAME database.
// By updating the data in the existing db, the audit listener gets triggered,
// and inserts your "initial" data in the audit tables.
for( Foo foo: all ) {
    session.replicate( foo, ReplicationMode.OVERWRITE ); 
}     
SessionFactory默认SessionFactory;
//特殊配置的sessionfactory,带有envers audit侦听器+拦截器
//它将所有属性标记为脏,即使它们不是脏的。
会话工厂复制会话工厂;
//必须使用不同的会话工厂检索实体,否则
//审核表不会更新。(这可能是因为我做了什么
//错了,我不知道,但我知道如果你按照上面所说的做的话,它是有效的。感觉一下
//(可自由改进)
FooDao FooDao=新FooDao();
fooDao.setSessionFactory(defaultSessionFactory);
List all=fooDao.findAll();
//清理并关闭fooDao的连接。
..
//在此处从replicationSessionFactory获取会话,例如。
会话会话=replicationSessionFactory.getCurrentSession();
//复制所有数据,如果该id的en条目已存在,则覆盖数据
//诀窍是让两个会话工厂指向同一个数据库。
//通过更新现有数据库中的数据,将触发审计侦听器,
//并在审核表中插入“初始”数据。
for(Foo-Foo:all){
replicate(foo,ReplicationMode.OVERWRITE);
}     
我的数据源配置(通过Spring):


com.foo**
..
稽核_
拦截器:

import org.hibernate.EmptyInterceptor;
import org.hibernate.type.Type;
..

public class DirtyCheckByPassInterceptor extends EmptyInterceptor {

  public DirtyCheckByPassInterceptor() {
    super();
  }


  /**
   * Flags ALL properties as dirty, even if nothing has changed. 
   */
  @Override
  public int[] findDirty( Object entity,
                      Serializable id,
                      Object[] currentState,
                      Object[] previousState,
                      String[] propertyNames,
                      Type[] types ) {
    int[] result = new int[ propertyNames.length ];
    for ( int i = 0; i < propertyNames.length; i++ ) {
      result[ i ] = i;
    }
    return result;
  }
}
import org.hibernate.EmptyInterceptor;
导入org.hibernate.type.type;
..
公共类DirtyCheckByPassInterceptor扩展了EmptyInterceptor{
公共目录checkbypassinterceptor(){
超级();
}
/**
*将所有属性标记为脏,即使没有任何更改。
*/
@凌驾
public int[]finddrity(对象实体,
可序列化id,
对象[]当前状态,
对象[]以前的状态,
字符串[]属性名称,
类型[]类型){
int[]结果=新的int[propertyNames.length];
for(int i=0;i

请记住,这是一个简化的例子。它不会开箱即用,但会引导您找到一个可行的解决方案。

如果您使用的是Envers,并且数据不是在启用Envers的情况下创建的,则这类数据将出现问题

在我们的例子(Hibernate 4.2.8.Final)中,一个基本对象更新抛出“无法更新实体和的先前版本”(记录为[org.Hibernate.AssertionFailure]hh000099)

我花了一段时间才发现此讨论/解释如此交叉发布:


您是如何让审计工作正常进行的?我甚至无法做到这一点:(非常简单,只需阅读相当简短的手册:我想知道这一点,但我需要有关环境的审核信息。是哪个用户和他们“如何”进行了更改-他们所做的哪些高级用户操作触发了更改。这对于能够看到显式更改和。“副作用"更改。您知道envers是否处理了此需求吗?您能进一步扩展吗?您是说如果修订实体为null,那么您应该只使用AuditQuery返回的数组的第一个元素吗?这种方法已经为我解决了
javax.persistence.EntityNotFoundException:无法找到id为x的
异常特别是当正确加载历史审计的子数据时,对“静态”数据的引用(在Hibernate之外加载)会导致此异常。(目前Hibernate 4.2)。
import org.hibernate.EmptyInterceptor;
import org.hibernate.type.Type;
..

public class DirtyCheckByPassInterceptor extends EmptyInterceptor {

  public DirtyCheckByPassInterceptor() {
    super();
  }


  /**
   * Flags ALL properties as dirty, even if nothing has changed. 
   */
  @Override
  public int[] findDirty( Object entity,
                      Serializable id,
                      Object[] currentState,
                      Object[] previousState,
                      String[] propertyNames,
                      Type[] types ) {
    int[] result = new int[ propertyNames.length ];
    for ( int i = 0; i < propertyNames.length; i++ ) {
      result[ i ] = i;
    }
    return result;
  }
}