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

Hibernate多主键外键关联问题:Table1.save()无法正常POST/PUT

Hibernate联合主键关联保存问题排查与修复

表结构定义

CREATE TABLE table1
(
    id BIGSERIAL PRIMARY KEY NOT NULL,
    param1            varchar(255)
);

CREATE TABLE table2
(
    id_table1         BIGINT NOT NULL,
    pk2 varchar(255) NOT NULL,
    param2 varchar(255) NOT NULL,
    CONSTRAINT fk_table1 FOREIGN KEY (id_table1) REFERENCES merging_configuration(id)
);

ALTER TABLE merging_configuration_type
    ADD PRIMARY KEY (id_table1, pk2);

注:SQL存在两处笔误,REFERENCES merging_configuration(id)应改为REFERENCES table1(id),ALTER TABLE merging_configuration_type应改为ALTER TABLE table2,需修正后保证表结构逻辑正确。

现有Hibernate实体类

Table1实体

@Entity(name = "table1")
public class Table1 {

  @Id
  private Long id;

  private String param1;

  @OneToMany
  @JoinColumn(name = "id_table1")
  private List<Table2> table2list;

  // 构造方法、getter、setter…
}

Table2实体

@Entity(name = "table2")
public class Table2 {

  @EmbeddedId
  private Table2Id id;

  private String param2;

}

Table2Id嵌入主键类

@Embeddable
public class Table2Id implements Serializable {

  @Column(name = "id_table1")
  private Long idTable1;

  @Column(name = "pk2")
  private String pk2;

}

遇到的问题

  • 当table2List中的联合键与数据库现有数据一致时:param1可正常更新,但param2无法更新;
  • 当table2List不包含数据库现有数据时:日志报错ERROR: a NULL value violates the NOT NULL constraint of the column "id_table1" in the relation "table2". Meaning: The failed row contains (null, myPK2, myParam2);
  • 当table2List包含数据库不存在的数据时:save方法抛出错误Unable to find path.Table2 with id Table2Id(idTableB=1, pk2=myPK2)。

问题原因与修复方案

核心问题分析

  1. 未配置级联策略:@OneToMany默认无任何级联操作,导致Table1的save操作无法同步到关联的Table2;
  2. 联合主键与外键未绑定:Table2的联合主键中id_table1未与Table1主键建立映射,Hibernate无法自动填充外键值;
  3. 嵌入主键类未重写equals/hashCode:Serializable类型的嵌入主键必须实现这两个方法,否则Hibernate无法正确识别实体唯一性;
  4. 集合未初始化:未初始化的集合可能导致空指针或Hibernate无法正确处理关联关系。

修改后的实体类代码

Table1实体

@Entity(name = "table1")
public class Table1 {

  @Id
  @GeneratedValue(strategy = GenerationType.IDENTITY) // 适配BIGSERIAL自增主键
  private Long id;

  private String param1;

  @OneToMany(cascade = CascadeType.ALL, orphanRemoval = true)
  @JoinColumn(name = "id_table1", referencedColumnName = "id")
  private List<Table2> table2list = new ArrayList<>(); // 初始化集合

  // 构造方法、getter、setter…
}
  • cascade = CascadeType.ALL:让Table1的增删改查操作级联到关联的Table2;
  • orphanRemoval = true:自动删除从table2list中移除的Table2实体;
  • @GeneratedValue:让Hibernate自动获取数据库生成的自增主键。

Table2实体

@Entity(name = "table2")
public class Table2 {

  @EmbeddedId
  private Table2Id id;

  private String param2;

  @MapsId("idTable1") // 将嵌入主键中的idTable1与当前关联的Table1主键绑定
  @ManyToOne(fetch = FetchType.LAZY)
  @JoinColumn(name = "id_table1", insertable = false, updatable = false) // 避免重复维护外键字段
  private Table1 table1;

  // 用于创建关联实体的构造方法
  public Table2(Table1 table1, String pk2) {
    this.id = new Table2Id(table1.getId(), pk2);
    this.table1 = table1;
  }

  // Hibernate必需的无参构造方法
  public Table2() {}

  // getter、setter…
}
  • @MapsId:将嵌入主键中的idTable1字段与Table1的主键关联,Hibernate会自动填充该值;
  • 新增带参构造方法:确保创建Table2时正确初始化联合主键与关联关系。

Table2Id嵌入主键类

@Embeddable
public class Table2Id implements Serializable {

  @Column(name = "id_table1")
  private Long idTable1;

  @Column(name = "pk2")
  private String pk2;

  // 无参构造方法
  public Table2Id() {}

  // 带参构造方法
  public Table2Id(Long idTable1, String pk2) {
    this.idTable1 = idTable1;
    this.pk2 = pk2;
  }

  // 必须重写equals和hashCode,确保实体唯一性判断正确
  @Override
  public boolean equals(Object o) {
    if (this == o) return true;
    if (o == null || getClass() != o.getClass()) return false;
    Table2Id table2Id = (Table2Id) o;
    return Objects.equals(idTable1, table2Id.idTable1) && Objects.equals(pk2, table2Id.pk2);
  }

  @Override
  public int hashCode() {
    return Objects.hash(idTable1, pk2);
  }

  // getter、setter…
}

使用示例

保存或更新Table1时,需确保关联的Table2正确绑定:

// 创建Table1实体
Table1 table1 = new Table1();
table1.setParam1("updated_param");

// 创建关联的Table2实体(已有数据更新)
Table2 existingTable2 = new Table2(table1, "existing_pk2");
existingTable2.setParam2("updated_param2");

// 创建新的Table2实体
Table2 newTable2 = new Table2(table1, "new_pk2");
newTable2.setParam2("new_param2");

// 添加到集合
table1.getTable2list().add(existingTable2);
table1.getTable2list().add(newTable2);

// 保存操作
table1Repository.save(table1);

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.19 13:05:28