使用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 )
遇到的问题
- 编译时错误:
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)
- 添加提示的
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
相关产品推荐
相关产品推荐

