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

Hibernate 6 Envers:如何自定义@ElementCollection(@CompositeType)的审计表?

问题:Spring Boot 3.0迁移后Hibernate Envers无法正确审计复合类型集合

我正在将项目从Spring Boot 2.7.x迁移至3.0.x,其中一个实体包含复合类型的集合,核心代码如下:

实体与复合类型定义

@Entity
@Audited
public class Specialist {

    @Id
    @GeneratedValue
    private Long id;

    @ElementCollection
    @CompositeType(QualificationConverter.class)
    private Set<Qualification> qualifications;

    // ... 其他属性与方法
}

public class Qualification implements Serializable {
    private LocalDate date = LocalDate.now();
    private String customData;
    // ... getter、setter及builder方法
}

public class Expert extends Qualification { /* ... */ }
public class Professional extends Qualification { /* ... */ }

复合类型转换器

public class QualificationConverter implements CompositeUserType<Qualification> {

    @Override
    public Object getPropertyValue(Qualification qualification, int i) throws HibernateException {
        return switch (i) {
            case 0 -> qualification.getCustomData();
            case 1 -> qualification.getDate();
            case 2 -> qualification.getClass().getName();
            default -> null;
        };
    }

    @Override
    public Qualification instantiate(ValueAccess valueAccess, SessionFactoryImplementor sessionFactoryImplementor) {
        String type = valueAccess.getValue(2, String.class);
        try {
            QualificationBuilder builder = (QualificationBuilder) Class.forName(type).getMethod("builder").invoke(null);
            return builder.customData(valueAccess.getValue(0, String.class))
                    .date(valueAccess.getValue(1, LocalDate.class))
                    .build();
        } catch (Exception ex) {
            throw new HibernateException(ex);
        }
    }

    @Override
    public Class<?> embeddable() {
        return QualificationMapper.class;
    }

    @Override
    public Class<Qualification> returnedClass() {
        return Qualification.class;
    }

    // ... 其他必要方法实现

    public static class QualificationMapper {
        String customData;
        LocalDate date;
        String type;
    }
}

当前问题

启动应用后,Envers生成的审计映射仅包含SETORDINAL列,而非预期的customData、date、type列:

<hibernate-mapping xmlns="http://www.hibernate.org/xsd/orm/hbm">
    <class table="specialist_qualifications_AUD" entity-name="specialist_qualifications_AUD">
        <composite-id name="originalId">
            <key-many-to-one class="org.hibernate.envers.DefaultRevisionEntity" name="REV">
                <column name="REV"/>
            </key-many-to-one>
            <key-property name="REVTYPE" type="org.hibernate.envers.internal.entities.RevisionTypeType"/>
            <key-property name="Specialist_id">
                <column name="specialist_id"/>
                <type name="long">
                    <!-- 类型参数省略 -->
                </type>
            </key-property>
            <key-property name="SETORDINAL" type="integer">
                <column name="SETORDINAL"/>
            </key-property>
        </composite-id>
    </class>
</hibernate-mapping>

此前使用Spring Boot 2.7.x及旧版@Type(Def)注解时,该功能正常工作。


解决方案

1. 实现AuditCompositeUserType接口

Hibernate 6中,Envers对复合用户类型的审计逻辑有变更,仅实现CompositeUserType不再足够,需要实现**org.hibernate.envers.AuditCompositeUserType**(它是CompositeUserType的子接口),明确提供审计时的属性值获取逻辑:

public class QualificationConverter implements AuditCompositeUserType<Qualification> {
    // 保留原有CompositeUserType的所有实现方法

    // 新增AuditCompositeUserType要求的方法
    @Override
    public Object getAuditValue(Qualification qualification, String propertyName) {
        return switch (propertyName) {
            case "customData" -> qualification.getCustomData();
            case "date" -> qualification.getDate();
            case "type" -> qualification.getClass().getName();
            default -> null;
        };
    }
}

2. 确保映射类属性与审计列匹配

确认QualificationMapper的属性名(customData、date、type)和类型与数据库列完全对应,Envers依赖这个类生成审计表的列结构。

3. 验证Envers基础配置

检查spring.jpa.properties.org.hibernate.envers.audit_table_suffix配置是否正确(默认值_AUD与当前场景匹配),避免其他自定义配置干扰复合类型的审计逻辑。

4. 兼容回退方案(可选)

如果上述方法未生效,可以尝试使用Hibernate 6兼容旧版注解的方式,替换@CompositeType为@Type并配合@TypeDef:

@Entity
@Audited
@TypeDef(name = "qualificationType", typeClass = QualificationConverter.class)
public class Specialist {
    // ...
    @ElementCollection
    @Type(type = "qualificationType")
    private Set<Qualification> qualifications;
    // ...
}

原理说明

Spring Boot 3.0默认集成Hibernate 6,Envers在该版本中对复合用户类型的审计要求更明确:旧版仅通过CompositeUserType就能自动识别并生成对应审计列的逻辑已被调整,必须通过AuditCompositeUserType接口明确告知Envers如何提取复合类型的属性值用于审计,才能生成包含复合类型属性列的审计表。

内容的提问来源于stack exchange,提问作者F. Labusch

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.28 12:22:58