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

Hibernate:如何将外键映射到复合主键的一部分

在Hibernate中实现外键映射到复合主键的一部分

没问题,我帮你梳理下这个场景的实现步骤,其实只要处理好实体类的注解配置就很清晰了:

1. 先定义TABLE_A的复合主键类

因为TABLE_A的主键是COMPANY_ID和SERVICE_ID组成的复合主键,我们需要先创建一个可嵌入的主键类(用@Embeddable注解),这是Hibernate处理复合主键的标准方式之一:

import jakarta.persistence.Embeddable;
import java.util.Objects;

@Embeddable
public class TableAPK implements Serializable {
    // 对应TABLE_A的COMPANY_ID
    private String companyId;
    // 对应TABLE_A的SERVICE_ID
    private String serviceId;

    // Hibernate要求主键类必须有无参构造函数
    public TableAPK() {}

    // 方便创建实例的带参构造函数
    public TableAPK(String companyId, String serviceId) {
        this.companyId = companyId;
        this.serviceId = serviceId;
    }

    // 必须重写equals和hashCode,Hibernate用来判断主键是否唯一
    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        TableAPK tableAPK = (TableAPK) o;
        return Objects.equals(companyId, tableAPK.companyId) &&
                Objects.equals(serviceId, tableAPK.serviceId);
    }

    @Override
    public int hashCode() {
        return Objects.hash(companyId, serviceId);
    }

    // getter和setter方法
    public String getCompanyId() { return companyId; }
    public void setCompanyId(String companyId) { this.companyId = companyId; }
    public String getServiceId() { return serviceId; }
    public void setServiceId(String serviceId) { this.serviceId = serviceId; }
}

2. 定义TABLE_A的实体类

接下来创建TABLE_A对应的实体类,用@EmbeddedId注解嵌入刚才定义的复合主键类:

import jakarta.persistence.*;

@Entity
@Table(name = "TABLE_A")
public class TableA {
    @EmbeddedId
    private TableAPK id;

    // 这里添加TABLE_A的其他列,比如:
    private String otherColumn;

    // 无参构造函数
    public TableA() {}

    // 带主键的构造函数
    public TableA(TableAPK id) {
        this.id = id;
    }

    // getter和setter方法
    public TableAPK getId() { return id; }
    public void setId(TableAPK id) { this.id = id; }
    public String getOtherColumn() { return otherColumn; }
    public void setOtherColumn(String otherColumn) { this.otherColumn = otherColumn; }
}

3. 定义TABLE_B的实体类(核心部分)

TABLE_B需要把外键关联到TABLE_A复合主键中的COMPANY_ID,这里我们可以通过@ManyToOne结合@JoinColumn来实现,同时可以选择单独维护COMPANY_ID字段方便直接操作:

import jakarta.persistence.*;

@Entity
@Table(name = "TABLE_B")
public class TableB {
    @Id
    @Column(name = "ID")
    private Long id;

    // 关联TABLE_A的COMPANY_ID字段
    @ManyToOne
    @JoinColumn(
        name = "COMPANY_ID",
        referencedColumnName = "COMPANY_ID", // 指定关联TABLE_A复合主键中的COMPANY_ID
        insertable = false,
        updatable = false
    )
    private TableA tableA;

    // 单独维护COMPANY_ID字段,方便直接赋值/查询,避免每次都要通过tableA实例获取
    @Column(name = "COMPANY_ID")
    private String companyId;

    // TABLE_B的其他列
    private String otherColumnB;

    // 无参构造函数
    public TableB() {}

    // getter和setter方法
    public Long getId() { return id; }
    public void setId(Long id) { this.id = id; }
    public TableA getTableA() { return tableA; }
    public void setTableA(TableA tableA) { this.tableA = tableA; }
    public String getCompanyId() { return companyId; }
    public void setCompanyId(String companyId) { this.companyId = companyId; }
    public String getOtherColumnB() { return otherColumnB; }
    public void setOtherColumnB(String otherColumnB) { this.otherColumnB = otherColumnB; }
}

关键细节说明

  • 关于insertable和updatable:因为我们已经单独维护了companyId字段,所以把这两个属性设为false,避免Hibernate在插入或更新TableB时重复操作COMPANY_ID字段。如果不想单独维护这个字段,也可以删掉它,直接通过tableA.getId().getCompanyId()来获取,但插入TableB时需要先关联一个有效的TableA实例。
  • 复合主键的要求:复合主键类必须实现Serializable接口,并且重写equals和hashCode方法,这是Hibernate识别主键唯一性的必要条件。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.25 06:59:42