2

これが私のユースケースです

Personn と Email (@OneToMany 関係) の 2 つのエンティティがあります。どちらも監査を受けています。

最初に、電子メールを使用して新しい Personn を作成します (=> どちらもリビジョン 1 です)。次に、電子メールを変更します (=> 電子メールにはリビジョン 2 がありますが、Personn にはリビジョン 1 しかありません)。

Web アプリケーションでは、エンドユーザーは、ユーザーの属性と電子メールの属性を表示するビューを 1 つしか持っていません。このビューでは、この Personn の既存のすべてのリビジョンを表示したいと考えています。しかし、監査システムにクエリを実行すると、Personn が変更されていないため、リビジョン 2 が表示されません。

技術的な問題は理解していますが、エンドユーザーの観点から見ると、彼は担当者の電子メールを変更したため、リビジョン 2 を見たいと考えています。彼は、私たちがこれらの情報を 2 つの Java オブジェクトに分割することを決定したことを知りません (知る必要もありません)。もちろん、この問題は Personn-Email の関係だけではありません (私は Personn と、同じビューに表示される他のオブジェクトとの間に多くの関係を持っています - 住所、仕事、サイト、カードなど)

私は2つの解決策について考えました:

1- リビジョンが存在するかどうかを知るためにすべてのリレーションを照会します (ただし、大きなリクエストまたは複数のリクエストが生成されると思います - 私はたくさんのリレーションを持っています)。

2-「hibernate.listeners.envers.autoRegister」を false に設定し、独自の EnversIntegrator とイベントの実装を作成します。イベント実装 (デフォルトの Envers 実装をオーバーライドします) では、Email の属性が変更されたときに Personn 用の ModWorkUnit を作成します (もちろん、ハード コードされていません: personn フィールドに @AuditedPropagation のようなカスタム アノテーションを付けます)。このソリューションの欠点は、変更されていない場合でも、Personn の行が大量に作成されることです。

これらのソリューションについてどう思いますか? そのようなユースケースを解決するためのより良い方法を知っていますか?

アドバイスありがとうございます。

4

2 に答える 2

0

私は2番目の解決策を実装しようとしました:

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

うまくいくようですが、もう少しテストする必要があります。

于 2012-05-29T15:52:25.197 に答える
0

カスタム投稿更新リスナー ソリューションを機能させることができませんでした。addCollectionChangeWorkUnit は、プライベートとマークされている hibernate 4.1 まで存在しないようです。EnversPostUpdateEventListenerImpl は、休止状態 4.0 のある時点で表示されるようです

あなたの A エンティティに相当するものに非表示の 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();
        }
    }
}

これにより、多くのエンティティでカスタム コードが必要になる場合でも、リビジョンが B に追加されるたびに A にリビジョンが追加されます。

于 2012-06-28T12:01:06.677 に答える