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

如何使用Hibernate Envers为审计表添加操作类型字符串列

Hibernate Envers审计表新增可读操作类型字段实现

默认Hibernate Envers的审计表用revtype字段存数字格式的操作类型,固定映射关系为:0=INSERT(创建)、1=UPDATE(修改)、2=DELETE(删除),完全可以新增一列存UPDATE/DELETE/INSERT格式的字符串值,以下是三种常用实现方式,按需选择即可。


方案1:全局持久化字符串列(推荐,一次配置全表生效)

这个方案会给所有带@Audited注解的实体对应的审计表,自动新增operation_type字符串列,数据落库时自动填充对应值,不影响原有revtype字段的逻辑。

  • 第一步:自定义Envers事件监听器,在增删改操作触发时,给审计记录追加自定义字段值:
@Component
public class CustomAuditOperationListener implements
        EnversPostInsertEventListener,
        EnversPostUpdateEventListener,
        EnversPostDeleteEventListener {

    private final AuditConfiguration auditConfiguration;

    public CustomAuditOperationListener(EntityManagerFactory entityManagerFactory) {
        this.auditConfiguration = AuditConfiguration.getFor(entityManagerFactory);
    }

    @Override
    public void onPostInsert(PostInsertEvent event) {
        fillOperationType(event.getSession(), event.getPersister().getEntityName(), event.getId(), "INSERT");
    }

    @Override
    public void onPostUpdate(PostUpdateEvent event) {
        fillOperationType(event.getSession(), event.getPersister().getEntityName(), event.getId(), "UPDATE");
    }

    @Override
    public void onPostDelete(PostDeleteEvent event) {
        fillOperationType(event.getSession(), event.getPersister().getEntityName(), event.getId(), "DELETE");
    }

    private void fillOperationType(EventSource session, String entityName, Object entityId, String opValue) {
        if (auditConfiguration.getEntCfg().isVersioned(entityName)) {
            session.getActionQueue().registerProcess(
                s -> {
                    AuditProcess auditProcess = auditConfiguration.getSyncManager().get(s);
                    auditProcess.addCustomValue(entityName, entityId, "operation_type", opValue);
                }
            );
        }
    }

    // 补全接口要求实现的其余默认方法即可
}
  • 第二步:注册自定义监听器,覆盖Envers默认的事件监听配置:
@Configuration
public class EnversCustomConfig {
    @Bean
    public HibernatePropertiesCustomizer registerAuditListener(CustomAuditOperationListener listener) {
        return props -> {
            props.put("hibernate.ejb.event.post-insert", listener);
            props.put("hibernate.ejb.event.post-update", listener);
            props.put("hibernate.ejb.event.post-delete", listener);
        };
    }
}
  • 第三步:给所有_aud后缀的审计表新增字段:operation_type VARCHAR(16) NOT NULL,重启服务后新增的审计记录就会自动填充对应字符串值。

方案2:单实体查询时动态计算(无需改表、无需写监听器)

如果只需要给个别实体返回可读的操作类型,不需要持久化存储字符串列,可以直接在实体里加计算字段,查询时自动基于revtype转义:

@Entity
@Audited
public class Product {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    private String productName;
    // 其余业务字段省略

    // 该字段不写入业务表,仅查询审计表时动态计算
    @NotAudited
    @Formula("(case revtype when 0 then 'INSERT' when 1 then 'UPDATE' when 2 then 'DELETE' end)")
    private String operationType;
}

注意:该方式不会在审计表里新增实际列,仅在查询审计数据时返回格式化后的字符串。


方案3:SQL层面临时转义(零代码改动)

如果只是临时查询审计数据需要可读格式,不需要改代码和表结构,直接写查询SQL时用CASE语句转义即可:

SELECT
    rev AS revision_id,
    revtype AS revision_type_code,
    CASE revtype
        WHEN 0 THEN 'INSERT'
        WHEN 1 THEN 'UPDATE'
        WHEN 2 THEN 'DELETE'
    END AS operation_type,
    -- 其余需要查询的审计字段
FROM product_aud;

注意:Envers的revtype取值是内置RevisionType枚举固定定义的,不存在版本兼容问题,你之前测试得到的操作类型和数字的对应关系是准确的。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.27 18:12:57