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

Hibernate Envers不支持AnyType类型的审计问题求助

解决Hibernate Envers 5.4.32.Final不支持AnyType审计的问题

问题背景

使用Hibernate与Hibernate Envers 5.4.32.Final时,通过@Any注解实现多态映射(关联抽象类Party的子类Contact/Warehouse),Envers无法审计AnyType类型字段,抛出以下异常:

org.hibernate.MappingException: Type not supported for auditing: org.hibernate.type.AnyType, on entity MyEntity, property 'destination'.
at org.hibernate.envers.configuration.internal.metadata.AuditMetadataGenerator.throwUnsupportedTypeException(AuditMetadataGenerator.java:764) ~[hibernate-envers-5.4.32.Final.jar:5.4.32.Final]
at org.hibernate.envers.configuration.internal.metadata.AuditMetadataGenerator.addValueInFirstPass(AuditMetadataGenerator.java:212) ~[hibernate-envers-5.4.32.Final.jar:5.4.32.Final]
at org.hibernate.envers.configuration.internal.metadata.AuditMetadataGenerator.addValue(AuditMetadataGenerator.java:316) ~[hibernate-envers-5.4.32.Final.jar:5.4.32.Final]
at org.hibernate.envers.configuration.internal.metadata.AuditMetadataGenerator.addProperties(AuditMetadataGenerator.java:350) ~[hibernate-envers-5.4.32.Final.jar:5.4.32.Final]

相关实体映射代码:

实体字段定义

@Any(
        metaDef = "PartyMetaDef",
        metaColumn = @Column( name = "dest_type" )
)
@JoinColumn( name = "dest_id" )
private Party destination;

AnyMetaDef定义

@AnyMetaDefs(
        @AnyMetaDef( name= "PartyMetaDef", metaType = "string", idType = "long",
                metaValues = {
                        @MetaValue(value = "C", targetEntity = Contact.class),
                        @MetaValue(value = "W", targetEntity = Warehouse.class)
                }
        )
)

解决方案

方案1:手动拆分字段审计(最简便可行)

Envers不支持AnyType,但可以直接审计存储类型和ID的基础字段,手动维护关联关系:

  1. 在实体中添加两个独立字段,映射到原dest_type和dest_id列,标记为可审计
  2. 将原destination字段改为@Transient,通过代码逻辑手动同步这三个字段

修改后的实体代码:

// 直接映射存储类型和ID的字段,由Envers审计
@Column(name = "dest_type")
@Audited
private String destType;

@Column(name = "dest_id")
@Audited
private Long destId;

// 原关联字段改为瞬态,不参与ORM映射,手动维护
@Transient
private Party destination;

// 添加同步逻辑,比如在setDestination时更新destType和destId
public void setDestination(Party destination) {
    this.destination = destination;
    if (destination != null) {
        this.destType = destination instanceof Contact ? "C" : "W";
        this.destId = destination.getId();
    } else {
        this.destType = null;
        this.destId = null;
    }
}

查询审计数据时,可通过destType和destId手动还原关联的Party实例。

方案2:自定义Envers类型扩展(深度定制)

通过扩展Envers的元数据生成逻辑,添加对AnyType的支持:

  1. 自定义AuditMetadataGenerator的子类,重写addValueInFirstPass方法,跳过AnyType的类型检查,或者手动处理其元数据
  2. 注册自定义的元数据生成器到Envers配置中

注意:该方案需要对Envers内部逻辑有一定了解,且版本兼容性较强,升级Hibernate时需要同步调整代码。

方案3:替换@Any为标准多态映射(业务允许时)

如果业务场景允许,将@Any替换为Hibernate标准的继承映射方式(如JOINED或SINGLE_TABLE),Envers天然支持这类多态审计:

  1. 修改抽象类Party的继承策略:
@Entity
@Inheritance(strategy = InheritanceType.JOINED) // 或SINGLE_TABLE
public abstract class Party {
    // 实体字段...
}
  1. 调整MyEntity中的关联字段为普通多态关联:
@ManyToOne // 或@OneToOne,根据业务关系调整
@JoinColumn(name = "dest_id")
@Audited
private Party destination;

该方案无需额外处理Envers,直接利用其原生多态审计支持,但需要调整数据库表结构。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.03 05:30:53