2016-03-21 90 views
0

我一直在寻找一种方法,使envers不记录任何实体,当我从上次记录没有修改时合并。 我发现这应该是Envers的正常行为(如果没有修改,不需要审核)。Hibernate envers创建一个记录,当没有变化

实体只有@Audited注释,但即使自上次审核以来没有任何更改,它们也会继续进行审核。 这是我persitence.xml配置:

<property name="org.hibernate.envers.revision_field_name" value="revision" /> 
<property name="org.hibernate.envers.revision_type_field_name" value="revision_type" /> 
<property name="org.hibernate.envers.revision_on_collection_change" value="false"/> 
<property name="org.hibernate.envers.store_data_at_delete" value="true"/> 

我发现这个Hibernate Envers: Auditing an object, calling merge on it, gives an audit record EVERY time even with no change?但没有答案。

我的一些equals()/hascode()方法只测试ID(主键),但我没有找到任何有关这可能相关的主题。

我也看到有一个新的参数来查看哪个字段发生了变化,但我不认为这与我的问题有关。

我正在使用Postgresql,如果有问题。

此行为的任何想法?我现在唯一的解决方案是通过entityManager获取实体并对它们进行比较(如果涉及到这一点,我将使用一些基于反射的API)。

回答

3

问题不是来自应用程序,而是来自代码本身。我们的实体有一个字段“lastUpdateDate”,该字段在每个merge()的当前日期设置。这种比较是在合并之后由envers完成的,所以这个字段自上次修订以来有所变化。

对于那些很好奇,版本之间的变化评估org.hibernate.envers.internal.entities.mapper.MultiPropertyMapper.map()(至少在4.3.3.Final),如果oldStatenewState之间有任何变化,则返回true。它根据所比较的属性使用特定的映射器。


编辑:我会把我如何解决问题,但Dagmar的解决方案也可以使用。然而我的矿可能有点复杂和肮脏。

我使用了Envers的EnversPostUpdateEventListenerImpl,如The official documentationvarious SO answers所述:我创建了我的并强制Envers使用它。

@Override 
public void onPostUpdate(PostUpdateEvent event) { 
    //Maybe you should try catch that ! 
    if (event.getOldState() != null) { 
     final EntityPersister entityPersister = event.getPersister(); 
     final String[] propertiesNames = entityPersister.getPropertyNames(); 

     for (int i = 0; i < propertiesNames.length; ++i) { 
      String propertyName = propertiesNames[i]; 
      if(checkProperty(propertyName){ 
       event.getOldState()[i] = event.getState()[i]; 
     } 
    } 
    // Normal Envers processing 
    super.onPostUpdate(event); 
} 

checkProperty(String propertyName)只是检查,如果它是一个更新日期属性(propertyName.endsWith("lastUpdateDate")因为这是他们是如何在我们的应用程序)。诀窍是,我将旧状态设置为新状态,所以如果这是我实体中唯一修改的字段,它将不会对它进行审计(使用envers持久保留它)。但是,如果有其他字段被修改,Envers将用这些修改的字段和正确的lastUpdateDate来审计实体。

我也有一个问题,oldState是hh:mm:ss没有设置的时间(只有零),并且新状态是设置小时的同一天。所以我用了一个类似的技巧:

Date oldDtEffet = (Date) event.getOldState()[i]; 
Date newDtEffet = (Date) event.getState()[i]; 
if(oldDtEffet != null && newDtEffet != null && 
     DateUtils.isDateEqualsWithoutTime(oldDtEffet,newDtEffet)){ 
    event.getOldState()[i] = event.getState()[i]; 
} 

(注:你必须重新实现所有的事件监听器,即使他们将继承探微类Envers,没有周转要确保org.hibernate.integrator.spi。集成商在您的应用程序中)

+0

感谢您对自己问题的回应!我面临着类似的问题,并且将更新与原始版本进行比较时遇到问题。我正在使用Hibernate MergeEventListener来设置我的跟踪列,但我的主要问题是MergeEvent“原始”对象与“实体”相同。即它已经应用了更新,因为它是一个托管对象。请你能分享你的解决方案吗? – Dagmar

+0

抱歉,我在您发表评论时无法使用。我很高兴你找到了一个解决方案,我会编辑我的答案,包括我做了什么来纠正我的问题,但你的解决方案似乎也很好。 – Asoub

0

好消息是,Hibernate Envers按预期工作 - 版本(AUD表中的条目)不会创建,除非可修改的属性被修改。

但是,在我们的应用程序中,我们实现了一个MergeEventListener,它在每个实体保存上更新跟踪字段(lastUpdated,lastUpdatedBy)。这导致Envers即使在没有对实体进行任何更改的情况下也可以创建新版本。

的解决办法是在年底很简单(我们) - 使用如何使用拦截器和事件由Hibernate的例子:http://docs.jboss.org/hibernate/core/3.6/reference/en-US/html/events.html

我们换成我们班实行PersistEventListenerMergeEventListener具有延伸EmptyInterceptor和一类覆盖onFlushDirty和onSave方法。

public class EntitySaveInterceptor extends EmptyInterceptor { 

    @Override 
    public boolean onFlushDirty(Object entity, Serializable id, Object[] currentState, Object[] previousState, String[] propertyNames, Type[] types) { 
    setModificationTrackerProperties(entity); 
    return super.onFlushDirty(entity, id, currentState, previousState, propertyNames, types); 
    } 

    @Override 
    public boolean onSave(Object entity, Serializable id, Object[] state, String[] propertyNames, Type[] types) { 
    setModificationTrackerProperties(entity); 
    return super.onSave(entity, id, state, propertyNames, types); 
    } 

    private void setModificationTrackerProperties(Object object) { 
    if (SecurityContextHolder.getContext() != null && SecurityContextHolder.getContext().getAuthentication() != null) { 
     Object principal = SecurityContextHolder.getContext().getAuthentication().getPrincipal(); 
     if (principal != null && principal instanceof MyApplicationUserDetails) { 
     User user = ((MyApplicationUserDetails) principal).getUser(); 
     if (object instanceof ModificationTracker && user != null) { 
      ModificationTracker entity = (ModificationTracker) object; 
      Date currentDateTime = new Date(); 
      if (entity.getCreatedDate() == null) { 
      entity.setCreatedDate(currentDateTime); 
      } 
      if (entity.getCreatedBy() == null) { 
      entity.setCreatedBy(user); 
      } 
      entity.setLastUpdated(currentDateTime); 
      entity.setLastUpdatedBy(user); 
     } 
     } 
    } 
    } 
} 

挂钩的EntitySaveInterceptor到Hibernate的JPA持久性单元

<?xml version="1.0" encoding="UTF-8"?> 
<persistence xmlns="http://java.sun.com/xml/ns/persistence" 
      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="2.0" 
      xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd"> 

    <persistence-unit name="myapplication" transaction-type="RESOURCE_LOCAL"> 
     <provider>org.hibernate.ejb.HibernatePersistence</provider> 
     <properties> 
      <property name="hibernate.ejb.interceptor" value="org.myapplication.interceptor.EntitySaveInterceptor" />   
      <property name="hibernate.hbm2ddl.auto" value="none"/> 
      <property name="hibernate.show_sql" value="false"/> 
     </properties> 
    </persistence-unit> 

</persistence> 

以及物品是否完整,这里是ModificationTracker接口:

public interface ModificationTracker { 

    public Date getLastUpdated(); 

    public Date getCreatedDate(); 

    public User getCreatedBy(); 

    public User getLastUpdatedBy(); 

    public void setLastUpdated(Date lastUpdated); 

    public void setCreatedDate(Date createdDate); 

    public void setCreatedBy(User createdBy); 

    public void setLastUpdatedBy(User lastUpdatedBy); 
} 

它也应该能够解决这个问题通过使用PreUpdateEventListener的实现来设置ModificationTracker值,因为该侦听器也仅在对象 是脏的。