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 です。