Hibernate Enver没有审核软删除中使用的字段?

时间:2018-05-09 04:21:11

标签: java hibernate hibernate-envers

我正在使用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中?

2 个答案:

答案 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数据