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

使用Hibernate Envers审计PostgreSQL自定义复合类型的问题求助

问题:使用Hibernate Envers审计PostgreSQL自定义类型时的字段重复与审计数据缺失

技术栈与环境配置

  • 技术栈:Quarkus、Kotlin、Panache Repository、Hibernate Envers
  • PostgreSQL自定义类型与表结构:
CREATE TYPE money_and_currency AS (amount integer, currency varchar(3));
CREATE TABLE example_table (
    "id" uuid,
    "maximum_value" money_and_currency,
    "minimum_value" money_and_currency
);
  • 初始实体类配置:
@Table(name = "example_table")
@Entity
@Audited
class ExpectedPaymentEntity {
    @Id
    @GeneratedValue
    @Column(columnDefinition = "uuid", updatable = false)
    var id: UUID? = null

    @Column(name = "maximum_value")
    @Embedded
    var maximumValue: MoneyAndCurrency ?= null

    @Column(name = "minimum_value")
    @Embedded
    var minimumValue: MoneyAndCurrency ?= null
}

@Embeddable
@Struct(name = "money_and_currency")
class MoneyAndCurrency(
    val amount: Int,
    val currency: String
)

遇到的问题

  1. 编译时错误:

Column 'amount' is duplicated in mapping for entity 'project.transaction.adapter.out.hibernate.entity.ExpectedPaymentEntity_AUD' (use '@Column(insertable=false, updatable=false)' when mapping multiple properties to the same column)

  1. 添加提示的insertable=false, updatable=false注解后,编译正常,但执行数据库操作时无审计数据生成。

问题根源

  • 错误地将@Embedded与@Struct结合使用,导致Hibernate把PostgreSQL复合类型展开为单独的amount、currency列;两个同类型嵌入式属性的子字段在审计表中重名,引发编译冲突。
  • 添加insertable=false, updatable=false会让Envers忽略这些字段的变更,因此无法生成审计数据。

解决方案

方案1:保留原复合类型表结构(推荐)

移除@Embedded注解,直接用@Struct映射PostgreSQL复合类型字段,让Hibernate将字段作为整体处理,避免审计表字段重复:

修改后的实体类

@Table(name = "example_table")
@Entity
@Audited
class ExpectedPaymentEntity {
    @Id
    @GeneratedValue
    @Column(columnDefinition = "uuid", updatable = false)
    var id: UUID? = null

    @Column(name = "maximum_value", columnDefinition = "money_and_currency")
    @Struct(name = "money_and_currency")
    var maximumValue: MoneyAndCurrency? = null

    @Column(name = "minimum_value", columnDefinition = "money_and_currency")
    @Struct(name = "money_and_currency")
    var minimumValue: MoneyAndCurrency? = null
}

修改后的复合类型类

@Struct(name = "money_and_currency")
class MoneyAndCurrency(
    val amount: Int,
    val currency: String
)

方案2:保留@Embeddable(需调整表结构)

如果需要在其他场景嵌入MoneyAndCurrency类,可通过@AttributeOverrides为每个嵌入式属性的子字段指定唯一列名,避免审计表冲突:

修改后的实体类

@Table(name = "example_table")
@Entity
@Audited
class ExpectedPaymentEntity {
    @Id
    @GeneratedValue
    @Column(columnDefinition = "uuid", updatable = false)
    var id: UUID? = null

    @Embedded
    @Struct(name = "money_and_currency")
    @AttributeOverrides(
        AttributeOverride(name = "amount", column = Column(name = "maximum_amount")),
        AttributeOverride(name = "currency", column = Column(name = "maximum_currency"))
    )
    var maximumValue: MoneyAndCurrency? = null

    @Embedded
    @Struct(name = "money_and_currency")
    @AttributeOverrides(
        AttributeOverride(name = "amount", column = Column(name = "minimum_amount")),
        AttributeOverride(name = "currency", column = Column(name = "minimum_currency"))
    )
    var minimumValue: MoneyAndCurrency? = null
}

修改后的复合类型类

@Embeddable
@Struct(name = "money_and_currency")
class MoneyAndCurrency(
    val amount: Int,
    val currency: String
)

注意:此方案需同步调整数据库表结构,将原复合类型列拆分为maximum_amount、maximum_currency、minimum_amount、minimum_currency四个单独列。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.03 01:56:13