首页 文章

使用来自Hibernate实体的现有数据填充修订表

提问于
浏览
19

我正在为现有的hibernate实体添加envers . 到目前为止,审计工作一切顺利,但查询是一个不同的问题,因为修订表没有填充现有数据 . 还有其他人已经解决了这个问题吗?也许您已经找到了一些方法来使用现有表填充修订表?我想我会问,我相信其他人会发现它很有用 .

5 回答

  • 1

    你不需要 .
    AuditQuery允许您通过以下方式获取RevisionEntity和数据修订:

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

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

  • 5

    我们通过运行一系列原始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 表示插入(与修改相对) .

  • 12

    如果您使用Envers ValidityAuditStrategy并且已创建除启用Envers之外的数据,则此类别中存在问题 .

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

    花了一些时间才发现这个讨论/解释,所以交叉发布:

    ValidityAuditStrategy with no audit record

  • 5

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

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

    我的数据源配置(通过Spring):

    <bean id="replicationDataSource" 
          class="org.apache.commons.dbcp.BasicDataSource" 
          destroy-method="close">
      <property name="driverClassName" value="org.postgresql.Driver"/>
      <property name="url" value=".."/>
      <property name="username" value=".."/>
      <property name="password" value=".."/>
      <aop:scoped-proxy proxy-target-class="true"/>
    </bean>
    
    <bean id="auditEventListener" 
          class="org.hibernate.envers.event.AuditEventListener"/>
    
    <bean id="replicationSessionFactory"
          class="o.s.orm.hibernate3.annotation.AnnotationSessionFactoryBean">
    
      <property name="entityInterceptor">
        <bean class="com.foo.DirtyCheckByPassInterceptor"/>
      </property>
    
      <property name="dataSource" ref="replicationDataSource"/>
      <property name="packagesToScan">
        <list>
          <value>com.foo.**</value>
        </list>
      </property>
    
      <property name="hibernateProperties">
        <props>
          ..
          <prop key="org.hibernate.envers.audit_table_prefix">AUDIT_</prop>
          <prop key="org.hibernate.envers.audit_table_suffix"></prop>
        </props>
      </property>
      <property name="eventListeners">
        <map>
          <entry key="post-insert" value-ref="auditEventListener"/>
          <entry key="post-update" value-ref="auditEventListener"/>
          <entry key="post-delete" value-ref="auditEventListener"/>
          <entry key="pre-collection-update" value-ref="auditEventListener"/>
          <entry key="pre-collection-remove" value-ref="auditEventListener"/>
          <entry key="post-collection-recreate" value-ref="auditEventListener"/>
        </map>
      </property>
    </bean>
    

    拦截器:

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

    ps:请记住,这是一个简化的例子 . 它不会开箱即用,但它会指导您找到一个有效的解决方案 .

  • 2

    看看http://www.jboss.org/files/envers/docs/index.html#revisionlog

    基本上,您可以使用@RevisionEntity批注定义自己的“修订类型”,然后实现RevisionListener接口以插入您的其他审计数据,如当前用户和高级操作 . 通常这些是从ThreadLocal上下文中提取的 .

相关问题