【问题标题】:Hibernate Enver doesn't audit the field which is using in the soft delete?Hibernate Enver 不审核软删除中使用的字段?
【发布时间】:2018-05-09 04:21:10
【问题描述】:

我正在使用 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 字段。 (Person_HISTORY 表中的deleted 字段为空)

知道在删除事件发生时如何将deleted 字段存储到 Person_HISTORY 中吗?

【问题讨论】:

    标签: java hibernate hibernate-envers


    【解决方案1】:

    简单来说,deleted的修改不包含在PostDeleteEvent中。

    Hibernate 在实体删除工作流开始时构建一次包含在PostDeleteEvent 中的状态。对于传统用法,这是有道理的,因为正在执行的语句是 DELETE,因此不会修改行。

    这里的难题是@SQLDelete 只是用自定义版本替换了实体持久化器使用的生成的DELETE 语句。一般来说,您提供的 SQL 可以是任何东西,只要它是语法上有效的 DML。

    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 以将其从审计架构中排除。

    【讨论】:

      【解决方案2】:

      很高兴知道@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 数据

      【讨论】:

      • 我看到flush会将对实体所做的任何更改提交到数据库中,那么flush+delete和save+delete一样吗?
      猜你喜欢
      • 1970-01-01
      • 2016-03-08
      • 1970-01-01
      • 2018-05-11
      • 2018-05-24
      • 2020-02-05
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多