我尝试记录我的 JPA 实体的任何更改。出于这个原因,每个实体都继承自一个抽象实体类,该实体类具有一个 LogEntry 对象列表。
抽象实体类:
@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 将被任何数据库操作调用(选择也会导致调用 preUpdate),所以我不能区分更改的对象和没有更改的对象。描述符事件、相关查询或 unitOfWork 提供的变更集在每种情况下都是空的。当前对象和旧对象的比较(由描述符事件提供)恕我直言太复杂了,不是吗?另一方面,在 preUpdateWithChanges 中,我可以轻松检测到更改的实体,但此时添加日志条目显然为时已晚。日志条目不会被持久化。
几乎每个试验都使我能够更改受影响实体的属性(如名称或 validTo)。但是没有解决方案提供创建新 LogEntry 实例的机会,或者更确切地说是持久化这个 LogEntry 实例。我还尝试通过 jndi 查找获取会话 bean 的实例,以手动保存 LogEntry。jndi 查找有效,但调用会话 bean 的创建或更新方法无效。
我当前的实体侦听器如下所示:
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。