Hibernate 如何检索已审核的关系修订?
这是我的用例 我有两个实体:个人和电子邮件(一种@OneToMany关系)。他们两人都经过审计 首先,我创建了一个新的Personn,其中包含一封电子邮件(=>他们都有修订版1),然后我修改了电子邮件(=>电子邮件有修订版2,但Personn只有修订版1) 在web应用程序中,最终用户只有一个视图来显示此人的属性以及他的电子邮件属性。在此视图中,我想显示此人的所有现有修订。但是,当我查询审计系统时,它不会显示修订版2,因为该人员尚未修改 我理解技术问题,但从最终用户的角度来看,他希望看到修订版2,因为他修改了该人员的电子邮件!他不知道(也不必知道)我们决定将这些信息分成两个Java对象。当然,这个问题不仅仅针对Personn电子邮件关系(我在Personn和同一视图中显示的其他对象之间有很多关系-地址、工作、站点、卡片等等) 我想到了两种解决方案: 1-查询所有关系以了解是否存在修订(但我认为它将生成一个大请求或多个请求-我有很多关系) 2-将“hibernate.listeners.envers.autoRegister”设置为false,编写自己的EnversIntegrator和事件实现。在事件实现(覆盖默认的Envers实现)中,当电子邮件的属性被修改时,我将为Personn创建一个ModWorkUnit(当然不会硬编码:perharps在Personn字段上创建一个自定义注释,如@AuditedPropagation)。 此解决方案的缺陷是即使未修改,也会为Personn创建大量行 您对这些解决方案有何看法?你知道解决这种用例的更好方法吗Hibernate 如何检索已审核的关系修订?,hibernate,hibernate-envers,Hibernate,Hibernate Envers,这是我的用例 我有两个实体:个人和电子邮件(一种@OneToMany关系)。他们两人都经过审计 首先,我创建了一个新的Personn,其中包含一封电子邮件(=>他们都有修订版1),然后我修改了电子邮件(=>电子邮件有修订版2,但Personn只有修订版1) 在web应用程序中,最终用户只有一个视图来显示此人的属性以及他的电子邮件属性。在此视图中,我想显示此人的所有现有修订。但是,当我查询审计系统时,它不会显示修订版2,因为该人员尚未修改 我理解技术问题,但从最终用户的角度来看,他希望看到修订版2
谢谢您的建议。我尝试实施第二种解决方案:
公共类修订CollectionPostUpdateEventListenerImpl扩展了EnversPostUpdateEventListenerImpl{
受保护的最终无效生成定向工作单元(AuditProcess AuditProcess、EntityPersister EntityPersister、String entityName、Object[]newState、,
对象[]旧状态,SessionImplementor会话){
//正在检查配置中是否已启用此功能。。。
如果(!getAuditConfiguration().getGlobalCfg().isGenerateRevisionsForCollections()){
回来
}
//检查实体的每个属性,如果它是一个与另一个实体的关系的“所有”。
//如果该属性的值已更改,并且关系是双向的,则会出现新的修订
//为相关实体生成。
字符串[]propertyNames=entityPersister.getPropertyNames();
for(int i=0;i
它似乎可以工作,但我还需要进行更多的测试。我尝试实施第二个解决方案:
公共类修订CollectionPostUpdateEventListenerImpl扩展了EnversPostUpdateEventListenerImpl{
受保护的最终无效生成定向工作单元(AuditProcess AuditProcess、EntityPersister EntityPersister、String entityName、Object[]newState、,
对象[]旧状态,SessionImplementor会话){
//正在检查配置中是否已启用此功能。。。
如果(!getAuditConfiguration().getGlobalCfg().isGenerateRevisionsForCollections()){
回来
}
//检查实体的每个属性,如果它是一个与另一个实体的关系的“所有”。
//如果该属性的值已更改,并且关系是双向的,则会出现新的修订
//为相关实体生成。
字符串[]propertyNames=entityPersister.getPropertyNames();
for(int i=0;ipublic class RevisionOnCollectionUpdateIntegrator implements Integrator {
private static final CoreMessageLogger LOG = Logger.getMessageLogger(CoreMessageLogger.class, RevisionOnCollectionUpdateIntegrator.class.getName());
public static final String REGISTER_ON_UPDATE = "org.hibernate.envers.revision_on_collection_update";
@Override
public void integrate(Configuration configuration, SessionFactoryImplementor sessionFactory, SessionFactoryServiceRegistry serviceRegistry) {
final boolean autoRegister = ConfigurationHelper.getBoolean(REGISTER_ON_UPDATE, configuration.getProperties(), true);
if (!autoRegister) {
LOG.debug("Skipping 'revision_on_collection_update' listener auto registration");
return;
}
EventListenerRegistry listenerRegistry = serviceRegistry.getService(EventListenerRegistry.class);
listenerRegistry.addDuplicationStrategy(EnversListenerDuplicationStrategy.INSTANCE);
final AuditConfiguration enversConfiguration = AuditConfiguration.getFor(configuration, serviceRegistry.getService(ClassLoaderService.class));
if (enversConfiguration.getEntCfg().hasAuditedEntities()) {
listenerRegistry.appendListeners(EventType.POST_UPDATE, new RevisionOnCollectionPostUpdateEventListenerImpl(enversConfiguration));
}
}
public class RevisionOnCollectionPostUpdateEventListenerImpl extends EnversPostUpdateEventListenerImpl {
protected final void generateBidirectionalWorkUnits(AuditProcess auditProcess, EntityPersister entityPersister, String entityName, Object[] newState,
Object[] oldState, SessionImplementor session) {
// Checking if this is enabled in configuration ...
if (!getAuditConfiguration().getGlobalCfg().isGenerateRevisionsForCollections()) {
return;
}
// Checks every property of the entity, if it is an "owned" to-one relation to another entity.
// If the value of that property changed, and the relation is bi-directional, a new revision
// for the related entity is generated.
String[] propertyNames = entityPersister.getPropertyNames();
for (int i = 0; i < propertyNames.length; i++) {
String propertyName = propertyNames[i];
RelationDescription relDesc = getAuditConfiguration().getEntCfg().getRelationDescription(entityName, propertyName);
if (relDesc != null && relDesc.isBidirectional() && relDesc.getRelationType() == RelationType.TO_ONE && relDesc.isInsertable()) {
// Checking for changes
Object oldValue = oldState == null ? null : oldState[i];
Object newValue = newState == null ? null : newState[i];
// Here is the magic part !!!!!!!!!
// The super class verify if old and new value (of the owner value) are equals or not
// If different (add or delete) then an audit entry is also added for the owned entity
// When commented, an audit row for the owned entity is added when a related entity is updated
// if (!Tools.entitiesEqual(session, relDesc.getToEntityName(), oldValue, newValue)) {
// We have to generate changes both in the old collection (size decreses) and new collection
// (size increases).
if (newValue != null) {
addCollectionChangeWorkUnit(auditProcess, session, entityName, relDesc, newValue);
}
if (oldValue != null) {
addCollectionChangeWorkUnit(auditProcess, session, entityName, relDesc, oldValue);
}
// }
}
}
}
@Entity
public class A {
private Date lastModified;
@OneToMany(mappedBy = "a", cascade = CascadeType.ALL )
private List<B> blist;
public void touch(){
lastModified=new Date();
}
}
public class B {
@ManyToOne
private A a;
@PreUpdate
public void ensureParentUpdated(){
if(a!=null){
a.touch();
}
}
}