You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Hibernate Enver未审计软删除字段?如何在删除时记录该字段

解决Hibernate Enver软删除时审计表不存储deleted字段的问题

首先咱们得先搞清楚问题根源:你用@SQLDelete实现软删除(把deleted设为当前时间),但Enver是在Hibernate触发删除事件的节点捕获实体状态的——这时候你的@SQLDelete还没执行,实体里的deleted字段还是null,所以审计表Person_HISTORY里这个字段自然是空的。

下面给你几个实用的解决方案,按推荐程度排序:

方案一:用@PreRemove提前设置deleted字段(最推荐)

直接在Person实体里加一个@PreRemove注解的方法,在Hibernate触发删除操作之前手动把deleted字段设为当前时间。这样Enver捕获实体状态时,就能拿到已经赋值的字段,存入审计表。

修改后的实体类代码:

@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;

    // 新增PreRemove方法,删除前设置deleted字段
    @PreRemove
    public void beforeRemove() {
        this.deleted = OffsetDateTime.now();
    }

    // 省略其他字段、getter和setter
}

这个方案的好处是:不需要改业务代码的删除逻辑,也不用额外配置Enver,只加几行代码就能解决问题。而且你的@SQLDelete还是用NOW()保证数据库层面的时间一致性,和代码里的OffsetDateTime.now()只要时区一致就完全匹配。

方案二:手动更新实体再执行删除(适合小项目)

如果不想改实体代码,也可以调整业务层的删除逻辑:不要直接调用entityManager.remove(person),而是先手动设置deleted字段,再执行更新操作。

示例代码:

// 原来的删除逻辑:entityManager.remove(person);
// 改成下面的更新逻辑
person.setDeleted(OffsetDateTime.now());
entityManager.merge(person);

这样Enver会把这次更新操作记录到审计表,deleted字段就会被正确存储。缺点是需要修改所有涉及删除Person的业务代码,适合项目规模较小的情况。

方案三:自定义Enver RevisionListener(适合复杂场景)

如果上面两种方案都不满足需求,可以自定义一个EntityTrackingRevisionListener,在Enver记录审计数据的节点,手动补全deleted字段的值。

  1. 先写自定义的Listener类:
import org.hibernate.envers.EntityTrackingRevisionListener;
import org.hibernate.envers.RevisionType;

public class PersonAuditRevisionListener implements EntityTrackingRevisionListener {

    @Override
    public void newRevision(Object revisionEntity) {
        // 这里可以初始化版本实体的属性,比如设置版本创建时间等(可选)
    }

    @Override
    public void entityChanged(Class<?> entityClass, String entityName, Object entityId, RevisionType revisionType, Object revisionEntity) {
        // 只处理Person实体的删除事件
        if (Person.class.equals(entityClass) && RevisionType.DEL.equals(revisionType)) {
            Person person = Person.class.cast(entity);
            // 如果deleted字段还是null,手动设置为当前时间
            if (person.getDeleted() == null) {
                person.setDeleted(OffsetDateTime.now());
            }
        }
    }
}
  1. 然后在Enver的配置里指定这个Listener:
properties.put("org.hibernate.envers.revision_listener", "com.yourpackage.PersonAuditRevisionListener");

这个方案灵活性最高,但配置稍复杂,适合需要对审计逻辑做更多自定义的场景。


内容的提问来源于stack exchange,提问作者Viet

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.27 04:27:25