java - persistencia - Rellene las tablas de revisión de envers con datos existentes de entidades de Hibernate
mapeo hibernate (5)
Estoy agregando envers a las entidades de hibernación existentes. Todo funciona sin problemas en lo que respecta a la auditoría, sin embargo, la consulta es un problema diferente porque las tablas de revisión no están llenas con los datos existentes. ¿Alguien más ya ha resuelto este problema? ¿Tal vez ha encontrado alguna forma de rellenar las tablas de revisión con la tabla existente? Solo pensé en preguntar, estoy seguro de que a otros les resultará útil.
Eche un vistazo a http://www.jboss.org/files/envers/docs/index.html#revisionlog
Básicamente, puede definir su propio ''tipo de revisión'' usando la anotación @RevisionEntity y luego implementar una interfaz RevisionListener para insertar sus datos de auditoría adicionales, como el usuario actual y la operación de alto nivel. Por lo general, se extraen de contexto ThreadLocal.
Hemos resuelto el problema de llenar los registros de auditoría con los datos existentes de la siguiente manera:
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 );
}
La configuración de mis fuentes de datos (a través de 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>
El interceptor:
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: ten en cuenta que este es un ejemplo simplificado. No funcionará de manera inmediata, pero lo guiará hacia una solución de trabajo.
Rellenamos los datos iniciales ejecutando una serie de consultas de SQL sin procesar para simular la "inserción" de todas las entidades existentes como si se hubieran creado al mismo tiempo. Por ejemplo:
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
Tenga en cuenta que el valor REVTYPE es 0 para indicar una inserción (en lugar de una modificación).
Tendrá un problema en esta categoría si utiliza Envers ValidityAuditStrategy y tiene datos que se crearon de otra manera que no sea con Envers habilitado.
En nuestro caso (Hibernate 4.2.8.Final), una actualización básica del objeto arroja "No se puede actualizar la revisión anterior para la entidad y" (registrado como [org.hibernate.AssertionFailure] HHH000099).
Tardé un rato en encontrar esta discusión / explicación, de modo que se publicara de forma cruzada:
Usted no necesita
AuditQuery le permite obtener tanto RevisionEntity como revisión de datos por:
AuditQuery query = getAuditReader().createQuery()
.forRevisionsOfEntity(YourAuditedEntity.class, false, false);
Esto construirá una consulta que devuelve una lista de Objeto [3]. El elemento principal es sus datos, el segundo es la entidad de revisión y el tercero es el tipo de revisión.