2016-03-21 95 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值,因爲該偵聽器也僅在對象 是髒的。