如何使用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
相关产品推荐
相关产品推荐

