6

JPAエンティティの変更をログに記録しようとしています。このため、各エンティティは、LogEntry オブジェクトのリストを持つ抽象エンティティ クラスから継承します。

AbstractEntity クラス:

@Entity
@Inheritance(strategy = InheritanceType.JOINED)
@EntityListeners(ChangeListener.class)
public abstract class AbstractEntity implements Serializable {

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Long id;
    @Version
    private Long version;
    @Temporal(TemporalType.DATE)
    private Date validFrom;
    @Temporal(TemporalType.DATE)
    private Date validTo;
    private String name;
    @OneToMany(cascade = CascadeType.ALL, mappedBy = "abstractEntity")
    private List<LogEntry> logEntry = new ArrayList<LogEntry>();
    //getter and setter
}

LogEntry クラス:

@Entity
public class LogEntry extends AbstractEntity {

    @ManyToOne
    @JoinColumn
    protected AbstractEntity abstractEntity;
    @ManyToOne
    @JoinColumn
    protected Person person; // creator or updater
    @Column(updatable=false, insertable=false, columnDefinition="TIMESTAMP DEFAULT   CURRENT_TIMESTAMP")
    @Temporal(TemporalType.TIMESTAMP)
    protected Date changeDate;
    protected String comment;
    //getter and setter
}

これに対する私のアプローチは、エンティティが更新または永続化される前に、新しい LogEntry オブジェクトを作成し、それをエンティティの LogEntry リストに追加することでした。

私は次の解決策を試しました:

  • コールバック アノテーション (@PreUpdate、@PrePersist など) をエンティティ クラスで直接使用するか、AbstractEntity に接続されたエンティティ リスナーで分離して使用する
  • EclipsLink の DescriptorEvent と対応するコールバック メソッドをエンティティ リスナーで使用します。これは最も有望な試験でした。preUpdate では、影響を受けるオブジェクトに新しい LogEntry を追加できました。追加された LogEntry も正しく永続化されましたが、preUpdate はデータベース操作によって呼び出されます (select も preUpdate の呼び出しにつながります)。そのため、変更されたオブジェクトと変更されていないオブジェクトを区別することはできません。ディスクリプタ イベント、関連するクエリ、または unitOfWork から提供された変更セットは、いずれの場合も null です。現在のオブジェクトと古いオブジェクト (記述子イベントによって提供される) の比較は複雑すぎますね。一方、preUpdateWithChanges では、変更されたエンティティを簡単に検出できましたが、この時点では明らかにログエントリを追加するには遅すぎます。ログエントリは保持されません。

これらの試行のほぼすべてで、影響を受けるエンティティの属性 (name や validTo など) を変更できます。しかし、新しい LogEntry インスタンスを作成したり、この LogEntry インスタンスを永続化したりする機会を提供するソリューションはありません。また、LogEntry を手動で永続化するために、jndi ルックアップを介してセッション Bean のインスタンスを取得しようとしました。jndi ルックアップは機能しますが、セッション Bean の create または update メソッドを呼び出しても効果はありません。

現在のエンティティ リスナーは次のようになります。

public class ChangeListener extends DescriptorEventAdapter {

    @Override
    public void preUpdate(DescriptorEvent event) {
        AbstractEntity entity = (AbstractEntity) event.getObject();
        if (!(entity instanceof LogEntry)) {
            LogEntry logEntry = new LogEntry();
            logEntry.setPerson(getSessionController().getCurrentUser());
            logEntry.setAbstractEntity(entity);
            entity.getLogEntries().add(logEntry);
        }
    }
}

Hibernate Envers は、さまざまな理由からオプションではありません。

EclipseLink のバージョンは 2.3.2 です。

4

4 に答える 4

8

コミット プロセスからのイベント中に新しいオブジェクトを永続化するのは難しい場合があります。

コミットする前に変更を取得し、ログを保持することができます (UnitOfWork から変更セットを取得します)。

http://wiki.eclipse.org/EclipseLink/FAQ/JPA#How_to_access_what_changed_in_an_object_or_transaction.3Fを参照して ください。

それ以外の場合は、イベント内から直接挿入してオブジェクトを作成することができます。

すなわち

event.getSession().insertObject(logEntry);
于 2013-04-01T14:43:13.613 に答える
4

preUpdate で現在のエンティティと古いエンティティを比較することで、一時的にこの問題を解決しました。比較は、Apache Commons LangのEqualsBuilderで行われます。

public class ChangeAbstractEntityListener extends DescriptorEventAdapter {

    @Override
    public void preUpdate(DescriptorEvent event) {
        AbstractEntity originalEntity = (AbstractEntity) event.getOriginalObject();
        AbstractEntity entity = (AbstractEntity) event.getObject();

        if (!EqualsBuilder.reflectionEquals(originalEntity, entity, true)) {
            if (!(entity instanceof LogEntry)) {
                LogEntry logEntry = new LogEntry();
                logEntry.setPerson(getSessionController().getCurrentUser());
                logEntry.setAbstractEntity(entity);
                entity.getLogEntries().add(logEntry);
            }
        }
    }
    //jndi lookup to get the user object
}
于 2013-05-17T08:20:05.533 に答える
0

履歴ポリシーを使用した変更の追跡を試すことができます

TopLinkは、データベースに加えられたすべての変更を追跡するための拡張サポートを提供します。EclipseLink HistoryPolicy を ClassDescriptor で構成して、任意の時点でのオブジェクトの状態を格納する元のミラー テーブルを格納できます。これは、監査の目的で使用したり、過去の時点でクエリを実行したり、古いデータを復元したりできます。

于 2016-06-03T09:26:15.183 に答える