You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

如何自定义Hibernate EnVers?实体审计库定制需求与方案咨询

Great question! Let's walk through how you can extend Hibernate EnVers to meet your exact needs—per-entity audit tables and JSON-based incremental diff logs instead of full snapshots.

First, a quick note: EnVers actually already creates a separate audit table for each entity (named like your_entity_AUD by default), so that requirement is partially covered right out of the box. The main work is modifying its behavior to store diffs instead of full entity snapshots. Here's an actionable plan:

Customizing Hibernate EnVers for JSON Diff Audit Logs

1. Implement a Custom AuditStrategy

The core of EnVers' audit logic is controlled by the AuditStrategy interface. We'll extend the default AbstractAuditStrategy to compute and store JSON diffs instead of full entity snapshots:

  • Create a class like JsonDiffAuditStrategy that extends AbstractAuditStrategy.
  • Override the perform method, which handles writing audit records to the database.
  • In this method, fetch the current entity state and its previous state (from EnVers' history store).
  • Use a JSON library (like Jackson with the jackson-diff module) to generate a structured diff between the two states.
  • Replace the full entity field insert with the JSON diff string, paired with standard revision metadata (revision ID, revision type: insert/update/delete).

Sample code snippet:

public class JsonDiffAuditStrategy extends AbstractAuditStrategy {
    private final ObjectMapper objectMapper = new ObjectMapper();
    private final JsonDiff diffGenerator = JsonDiff.builder().build();

    @Override
    public void perform(Session session, String entityName, AuditConfiguration auditCfg, Serializable id, Object data, Object revision) {
        // Retrieve the previous state of the entity from EnVers history
        Object previousState = getPreviousEntityState(session, auditCfg, entityName, id, revision);
        
        // Generate JSON diff
        JsonNode currentEntityNode = objectMapper.valueToTree(data);
        JsonNode diffNode = previousState != null 
            ? diffGenerator.diff(objectMapper.valueToTree(previousState), currentEntityNode)
            : currentEntityNode; // For new entities, store full state as initial record
        
        String diffJson = objectMapper.writeValueAsString(diffNode);
        
        // Build and persist the audit record with diff data
        AuditRecord auditRecord = new AuditRecord(id, ((RevisionEntity) revision).getId(), getRevisionType(data), diffJson);
        session.persist(auditRecord);
    }

    // Helper method to fetch previous entity state
    private Object getPreviousEntityState(Session session, AuditConfiguration auditCfg, String entityName, Serializable id, Object revision) {
        // Use EnVers' query API to fetch the most recent prior state
        AuditQuery query = auditCfg.getAuditReaderFactory().get(session).createQuery()
            .forEntitiesAtRevision(entityName, ((RevisionEntity) revision).getId() - 1);
        query.add(AuditEntity.id().eq(id));
        return query.getSingleResultOrNull();
    }
}

2. Customize Audit Table Structure

By default, EnVers creates audit tables that mirror the original entity's fields. We need to adjust this to store only revision metadata plus the JSON diff:

  • Use @AuditTable on your entities to explicitly define audit table names (optional but recommended for consistency).
  • Implement a CustomEntityDefinitionListener to intercept EnVers' audit entity creation process. This lets you replace all entity-specific fields with a single json_diff column (use TEXT or database-native JSON type, depending on your DB).

Example listener code:

public class JsonDiffAuditEntityListener implements CustomEntityDefinitionListener {
    @Override
    public void customize(EntityDefinition entityDefinition, AuditConfiguration auditCfg) {
        // Clear default mirrored fields
        entityDefinition.getAttributes().clear();
        
        // Add mandatory revision columns
        entityDefinition.addAttribute(
            AttributeDefinition.builder()
                .name("rev")
                .type(Long.class)
                .column(ColumnDefinition.builder().name("rev").build())
                .build()
        );
        entityDefinition.addAttribute(
            AttributeDefinition.builder()
                .name("revtype")
                .type(Integer.class)
                .column(ColumnDefinition.builder().name("revtype").build())
                .build()
        );
        
        // Add JSON diff column
        entityDefinition.addAttribute(
            AttributeDefinition.builder()
                .name("jsonDiff")
                .type(String.class)
                .column(ColumnDefinition.builder().name("json_diff").sqlType("JSON").build())
                .build()
        );
        
        // Add entity ID column (to link audit records to source entity)
        entityDefinition.addAttribute(
            AttributeDefinition.builder()
                .name("entityId")
                .type(Serializable.class)
                .column(ColumnDefinition.builder().name("entity_id").build())
                .build()
        );
    }
}

3. Configure EnVers to Use Your Custom Components

Update your Hibernate configuration to register the custom strategy and listener:

Add these properties to your persistence.xml or Hibernate properties file:

org.hibernate.envers.audit_strategy=com.yourpackage.JsonDiffAuditStrategy
org.hibernate.envers.custom_entity_definition_listener=com.yourpackage.JsonDiffAuditEntityListener

4. Handle Edge Cases

  • First-time inserts: For new entities, store the full entity JSON as the initial audit record (since there's no prior state to diff against).
  • Deletions: When deleting an entity, you might want to store the final state or a clear deletion marker in the JSON diff for auditing clarity.
  • Performance: Diff computation for large entities can add overhead. Consider caching recent entity states or optimizing diff logic to only track fields marked with a custom annotation (e.g., @AuditDiff).

Alternative Tools to Consider

If extending EnVers feels too involved, you might explore:

  • Custom Hibernate Interceptor: A lightweight approach where you implement a Interceptor to capture entity changes, compute diffs, and write to your own audit tables. This gives full control but requires more boilerplate.
  • Spring Data Envers: Built on top of EnVers with Spring Data integration, but still uses full snapshots by default—you'd still need to add the same custom diff logic.

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

火山引擎 最新活动