我正在使用hibernate.envers设置:
properties.put("org.hibernate.envers.audit_table_suffix", "_HISTORY");
properties.put("org.hibernate.envers.store_data_at_delete", "true");
主表如下:
@Entity
@Table(name = "person")
@SQLDelete(sql = "UPDATE person SET deleted = NOW() WHERE id = ?", check = ResultCheckStyle.COUNT)
@Where(clause = "deleted is NULL")
@Audited
public class Person {
@Id
private UUID id;
private OffsetDateTime deleted;
//omit other fields
}
当删除事件发生时,Person_HISTORY表将存储除deleted
字段之外的所有Person字段。 (deleted
表中的Person_HISTORY
字段为空)
在删除事件时,是否知道如何将deleted
字段存储到Person_HISTORY中?
答案 0 :(得分:1)
简而言之,deleted
的修改不包含在PostDeleteEvent
中。
Hibernate在实体删除工作流程的开头构建一次PostDeleteEvent
中包含的状态。对于传统用途,这是有道理的,因为正在执行的语句是DELETE
,因此该行未被修改。
这里的难题是@SQLDelete
只是替换了实体的persister与自定义版本一起使用的生成的DELETE
语句。一般来说,只要它是语法上有效的DML,您提供的SQL就可以是任何东西。
Hibernate不知道的是,在这种情况下,替换SQL是UPDATE
。因此,在持久性执行删除SQL语句并且 post -delete回调触发后,事件的状态不会发生更改,并且事件的状态不会发生更改,他们仍然在实体删除工作流程开始时被提供状态。
话虽如此,Envers确实在审计表中记录了REVTYPE=2
(又名DEL
)。因此,如果您需要删除时间戳,您仍然可以从Envers REVINFO
表中获取它。
更新
这里的另一个想法是使用两列而不是一列。不使用时间戳作为软删除指示符,而是使用位标志
@Entity
@Audited
@SQLDelete("UPDATE person SET deleted = true WHERE id = ?")
@Where(clause = "deleted is NULL or deleted != true")
public class Person {
@NotAudited
private boolean deleted;
private OffsetDateTime deleteTime;
@PreRemove
public void onPreRemove() {
this.deleteTime = ...;
}
}
在这里,我使用@PreRemove
设置删除时间值,然后将PostDeleteEvent
传递给Envers,让Hibernate管理位字段。由于事件状态中从未提供deleted
字段,因此我将其标记为@NotAudited
以将其从审核模式中排除。
答案 1 :(得分:1)
很高兴知道@Naros的解决方案,我们实际上遇到了同样的问题,这是我们的解决方案,更改服务代码
@Transactional
public void deletePerson(UUID id) {
PersonEntity personEntity= getPerson(id);
personEntity.setDeleted(OffsetDateTime.now());
personRepository.flush();
personRepository.delete(personEntity);
}
我们需要关注两个主要步骤:
@Transactional
)flush
它。不太好,但有效:D,PostDeleteEvent
会附带deleted
数据