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

如何使用非主键复合键关联JPA实体?

可以使用非主键复合键关联实体,但你的映射配置存在问题

问题原因分析

抛出的异常org.hibernate.AnnotationException: referencedColumnNames(domain_object_id, tenant_id) of ProductEntity.kind referencing KindEntity not mapped to a single property,核心原因是:
你在ProductEntity的@JoinColumns中指定的关联列,在KindEntity里是封装在TenantEntityDomainObjectTuple这个嵌入式对象中的,Hibernate无法直接识别分散在嵌入式对象中的多个字段作为关联目标;另外你的实体映射还存在数据库列名不匹配的问题(实体中写的wine_kind对应数据库表的kind列)。

解决方案

方案1:暴露目标实体的单独属性

在KindEntity中直接映射domain_object_id和tenant_id字段,让Hibernate能明确识别关联的目标属性:

@Entity
@Table(name = "kind", uniqueConstraints = @UniqueConstraint(columnNames = {"tenant_id", "domain_object_id"}))
public class KindEntity {
    @Id
    @GeneratedValue
    private long id;
    
    // 新增单独字段映射,与嵌入式对象共享数据库列
    @Column(name = "domain_object_id", nullable = false)
    private String domainObjectId;
    
    @Column(name = "tenant_id", nullable = false)
    private String tenantId;
    
    @NaturalId
    @Embedded
    @AttributeOverrides({ 
        @AttributeOverride(name = "domain_object_id", column = @Column(name = "domain_object_id", insertable = false, updatable = false)),
        @AttributeOverride(name = "tenant_id", column = @Column(name = "tenant_id", insertable = false, updatable = false))
    })
    private TenantEntityDomainObjectTuple domainObjectTuple;
    
    @Column(name = "display_name", nullable = false)
    private String displayName;
    
    // Getters & Setters
}

然后修正ProductEntity中的关联配置(修正列名错误+指向单独属性):

@Entity
@Table(name = "product")
public class ProductEntity {
    // ... 其他属性保持不变
    
    @ManyToOne
    @JoinColumns(value = {
            @JoinColumn(name = "kind", referencedColumnName = "domainObjectId", insertable = false, updatable = false),
            @JoinColumn(name = "tenant_id", referencedColumnName = "tenantId", insertable = false, updatable = false)
    }, foreignKey = @ForeignKey(name = "product_kind_tenant_id_fkey"))
    private KindEntity kind;
}

方案2:关联嵌入式对象的属性路径(Hibernate 5.2+支持)

如果不想新增单独字段,可以直接通过嵌入式对象的属性路径关联,referencedColumnName使用嵌入式属性名.属性的格式:

@Entity
@Table(name = "product")
public class ProductEntity {
    // ... 其他属性保持不变
    
    @ManyToOne
    @JoinColumns(value = {
            @JoinColumn(name = "kind", referencedColumnName = "domainObjectTuple.domainObjectId", insertable = false, updatable = false),
            @JoinColumn(name = "tenant_id", referencedColumnName = "domainObjectTuple.tenantId", insertable = false, updatable = false)
    }, foreignKey = @ForeignKey(name = "product_kind_tenant_id_fkey"))
    private KindEntity kind;
}

关键注意点

  1. 确保关联的目标列(kind表的tenant_id和domain_object_id)有唯一约束,这是Hibernate允许非主键关联的前提,你的数据库已经配置了unique (tenant_id, domain_object_id),实体中也可以通过@UniqueConstraint同步配置。
  2. 严格保持实体映射的列名与数据库表列名一致,避免拼写错误。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.20 05:35:29