2012-05-10 62 views
2

這是我的使用案例如何檢索經審計的關係修訂版?

我有兩個實體:Personn和Email(@OneToMany關係)。他們都接受審計。

首先我創建一個新的Personn,帶有一個電子郵件(=>他們都有一個修訂版本1),然後修改Email(=>電子郵件有一個修訂版本2,但是Personn只有修訂版本1)

在Web應用程序中,最終用戶只有一個視圖來顯示Personn的屬性以及他的電子郵件屬性。在這個視圖中,我想顯示此Personn的所有現有修訂。但是當我查詢審計系統時,由於Personn沒有被修改,所以它沒有向我顯示修訂版本2。

我明白技術問題,但是從最終用戶的角度來看,他想看到一個修訂版本2,因爲他修改了這個人的電子郵件!他不知道(也不必知道)我們決定將這些信息分成兩個Java對象。當然,這個問題不僅僅是Personn-Email關係(我在Personn和其他對象之間有很多關係,它們在同一個視圖中顯示 - 地址,作業,網站,卡片等等)

我想到了兩種解決方案:

1-查詢所有關係以確定修訂版是否存在(但我想它會生成一個大請求或多個請求 - 我有很多關係)。將「hibernate.listeners.envers.autoRegister」設置爲false,編寫我自己的EnversIntegrator和事件實現。在事件實現中(它覆蓋默認的Envers實現),當電子郵件的屬性被修改時,我將爲Personn創建一個ModWorkUnit(它當然不會被硬編碼:在personn字段上使用@AuditedPropagation的自定義註釋)。 這個解決方案的缺陷是爲Personn創建了很多行,即使它沒有被修改。

您對這些解決方案有何看法?你知道解決這種用例的更好方法嗎?

感謝您的建議。

+0

這可以幫助你: http://stackoverflow.com/questions/10121103/hibernate-envers-retrieving-the-right-revisions-of-an-entity-with-a-collection – user639466

+0

謝謝,我看到了這個問題和@adamw的回答:「當前無法獲得實體**或相關實體更改的修訂列表**」。正是這個問題:如何實現這個功能? –

+0

相關http://stackoverflow.com/questions/10697945/hibernate-envers-track-revisions-in-the-owning-side-of-a-onetomany-relation – Jean

回答

0

我試圖執行第二個方案:

  1. 首先我的積分增加了一個新的職位更新監聽器(RevisionOnCollectionPostUpdateEventListenerImpl)

    public 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)); 
        } 
    } 
    
  2. 然後在之後的更新監聽器(延伸) :

    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); 
           } 
         // } 
         } 
        } 
    } 
    

它似乎工作,但我必須測試更多一點。

+0

你使用哪個版本的hibernate和envers?我似乎無法找到所有進口... – Jean

0

我一直無法使自定義發佈後更新偵聽器解決方案的工作。 addCollectionChangeWorkUnit似乎不存在,直到休眠4.1它標記爲私有。 EnversPostUpdateEventListenerImpl似乎出現在休眠4.0的某個時間點

我解決了我的問題,在我的等效實體上添加了一個隱藏的lastUpdated日期字段。

@Entity 
public class A { 
    private Date lastModified; 
    @OneToMany(mappedBy = "a", cascade = CascadeType.ALL) 
    private List<B> blist; 
    public void touch(){ 
     lastModified=new Date(); 
    } 
} 

在相關實體(如你B場),我增加了以下內容:

public class B { 
    @ManyToOne 
    private A a; 

    @PreUpdate 
    public void ensureParentUpdated(){ 
     if(a!=null){ 
      a.touch(); 
     } 
    } 
} 

這確保了修訂加入A每當增加了補正,雖然它需要甚至B自定義代碼在許多實體中。