java 使用来自 Hibernate 实体的现有数据填充 envers 修订表
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/898529/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Populate envers revision tables with existing data from Hibernate Entities
提问by danieljimenez
I'm adding envers to an existing hibernate entities. Everything is working smoothly so far as far as auditing, however querying is a different issue because the revision tables aren't populated with the existing data. Has anyone else already solved this issue? Maybe you've found some way to populate the revision tables with the existing table? Just thought I'd ask, I'm sure others would find it useful.
我正在向现有的休眠实体添加 envers。就审计而言,一切都在顺利进行,但是查询是一个不同的问题,因为修订表没有填充现有数据。有没有其他人已经解决了这个问题?也许您已经找到了用现有表格填充修订表格的方法?只是想我会问,我相信其他人会觉得它很有用。
采纳答案by Vladimir Ivanov
You don't need to.
AuditQuery allows you to get both RevisionEntity and data revision by :
你不需要。
AuditQuery 允许您通过以下方式获取 RevisionEntity 和数据修订:
AuditQuery query = getAuditReader().createQuery()
.forRevisionsOfEntity(YourAuditedEntity.class, false, false);
This will construct a query which returns a list of Object [3]. Fisrt element is your data, the second is the revision entity and the third is the type of revision.
这将构造一个返回对象列表的查询 [3]。第一个元素是您的数据,第二个是修订实体,第三个是修订的类型。
回答by deryl
We populated the initial data by running a series of raw SQL queries to simulate "inserting" all the existing entities as if they had just been created at the same time. For example:
我们通过运行一系列原始 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
Note that the REVTYPEvalue is 0to indicate an insert (as opposed to a modification).
请注意,REVTYPE值为0表示插入(与修改相反)。
回答by user598656
You'll have a problem in this category if you are using Envers ValidityAuditStrategyand have data which has been created other than with Envers enabled.
如果您使用 Envers ValidityAuditStrategy并且创建的数据不是在启用 Envers 的情况下创建的,那么您将在此类别中遇到问题。
In our case (Hibernate 4.2.8.Final) a basic object update throws "Cannot update previous revision for entity and " (logged as [org.hibernate.AssertionFailure] HHH000099).
在我们的例子中(Hibernate 4.2.8.Final),一个基本的对象更新会抛出“无法更新实体和之前的修订版”(记录为 [org.hibernate.AssertionFailure] HHH000099)。
Took me a while to find this discussion/explanation so cross-posting:
我花了一段时间才找到这个讨论/解释,所以交叉发布:
回答by davidcyp
We have solved the issue of populating the audit logs with the existing data as follows:
我们已经解决了用现有数据填充审计日志的问题,如下所示:
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 );
}
The configuration of my data sources (via Spring):
我的数据源的配置(通过 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>
The 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: keep in mind that this is a simplified example. It will not work out of the box but it will guide you towards a working solution.
ps:请记住,这是一个简化的示例。它不会开箱即用,但会引导您找到可行的解决方案。
回答by Gregory Mostizky
Take a look at http://www.jboss.org/files/envers/docs/index.html#revisionlog
看看http://www.jboss.org/files/envers/docs/index.html#revisionlog
Basically you can define your own 'revision type' using @RevisionEntity annotation, and then implement a RevisionListener interface to insert your additional audit data, like current user and high level operation. Usually those are pulled from ThreadLocal context.
基本上,您可以使用@RevisionEntity 注释定义您自己的“修订类型”,然后实现一个 RevisionListener 接口来插入您的附加审计数据,例如当前用户和高级操作。通常这些是从 ThreadLocal 上下文中提取的。

