ملء جداول مراجعة المنبع مع البيانات الموجودة من كيانات السبات

StackOverflow https://stackoverflow.com/questions/898529

سؤال

أنا أضيف envers إلى كيانات السبات الحالية. كل شيء يعمل بسلاسة حتى الآن باعتباره المراجعة، مهما كان الاستعلام مشكلة مختلفة لأن جداول المراجعة غير مأهولة بالبيانات الموجودة. هل قام أي شخص آخر بحل هذه المشكلة بالفعل؟ ربما كنت قد وجدت طريقة لملء جداول المراجعة مع الجدول الحالي؟ فقط اعتقدت أنني سأطلب، أنا متأكد من أن الآخرين سيجدونها مفيدة.

هل كانت مفيدة؟

المحلول

أنت لا تحتاج إلى.
تتيح لك AuditQuery الحصول على كل من المراجع والبيانات بمراجعة:

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

سيبني هذا استعلام يرجع قائمة كائن [3]. عنصر FISRT هو بياناتك، والثاني هو كيان المراجعة والثالث هو نوع المراجعة.

نصائح أخرى

لقد سككت البيانات الأولية من خلال تشغيل سلسلة من استفسارات SQL RAW لمحاكاة "إدراج" جميع الكيانات الموجودة كما لو تم إنشاءها للتو في نفس الوقت. علي سبيل المثال:

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 للإشارة إلى إدراج (على عكس تعديل).

سيكون لديك مشكلة في هذه الفئة إذا كنت تستخدم envers ValidateAdstrategy. ولها بيانات تم إنشاؤها بخلاف تمكين ENVERS.

في حالتنا (السبات 4.2.8.Final) يرمي تحديث الكائن الأساسي "لا يمكن تحديث المراجعة السابقة للكيان و" (مسجلة باسم [Org.Abernate.assertionFailure] HHH000099).

أخذني بعض الوقت للعثور على هذا المناقشة / التفسير حتى النشر الشامل:

ValidateAditsTrategy مع عدم وجود سجل التدقيق

لقد حللت مسألة ملء سجلات التدقيق مع البيانات الموجودة كما يلي:

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

تكوين مصادر البيانات الخاصة بي (عبر الربيع):

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

ملاحظة: ضع في اعتبارك أن هذا مثال مبسط. لن يعمل خارج الصندوق ولكنه سوف يرشدك نحو حل العمل.

إلقاء نظرة على http://www.jboss.org/files/envers/docs/index.html#revisionlog.

أساسا يمكنك تحديد "نوع المراجعة" الخاص بك باستخدام التوضيحية Revisionentity، ثم قم بتطبيق واجهة RevisionListener لإدراج بيانات التدقيق الإضافية، مثل المستخدم الحالي وعملية عالية المستوى. عادة ما يتم سحب تلك السياق الخصر.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top