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

如何将复合键用作外键?(违反外键约束报错)

复合键作为外键时的插入异常问题

我在使用复合键作为外键时遇到了问题。现有数据结构是Country(国家)包含多个City(城市),每个City包含多个District(区县)。没有District表时,City的复合键能正常工作,但添加District表后,插入包含城市和区县的国家条目时触发以下错误:

ERROR: insert or update on table "district" violates foreign key constraint "fk_city_country"
Detail: Key (country_id, city_id)=(city01, country01) is not present in table "city".


相关代码

实体类代码

CountryEntity.java

// CountryEntity.java
@Entity
@Table(schema = "public", name = "country")
@NoArgsConstructor
@AllArgsConstructor
@Getter
@Setter
public class CountryEntity {
    @Id
    private String id;
 
    @OneToMany(cascade = {CascadeType.ALL}, fetch = FetchType.EAGER)
    @JoinColumn(name = "country_id")
    private List<CityEntity> cities;
}

CityEntity.java

// CityEntity.java
@Entity
@Table(schema = "public", name = "city")
@NoArgsConstructor
@AllArgsConstructor
@Getter
@Setter
public class CityEntity {
    @EmbeddedId
    private CityKey id;
 
    @OneToMany(cascade = {CascadeType.ALL}, fetch = FetchType.EAGER)
    @JoinColumn(name = "country_id")
    @JoinColumn(name = "city_id")
    private List<DistrictEntity> districts;
}

CityKey.java

// CityKey.java
@Embeddable
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
@EqualsAndHashCode
public class CityKey implements Serializable {
    @Column(name = "country_id")
    private String countryId;
 
    @Column(name = "district_id")
    private String districtId;
}

DistrictEntity.java

// DistrictEntity.java
@Entity
@Table(schema = "public", name = "district")
@NoArgsConstructor
@AllArgsConstructor
@Getter
@Setter
public class DistrictEntity {
    @Id
    public String id;
 
    @Column(name = "country_id")
    private String countryId;
 
    @Column(name = "city_id")
    private String cityId;
}

Liquibase 变更日志

# liquibase yaml changelog entry
- changeSet:
    id:  1
    author:  johnDoe
    changes:
      - createTable:
          tableName: country
          columns:
            - column:
                name: id
                type: varchar
                constraints:
                  nullable: false
                  primaryKey: true
      - createTable:
          tableName: city
          columns:
            - column:
                name: country_id
                type: varchar
                constraints:
                  nullable: false
                  references: country(id)
                  foreignKeyName: country_fk
            - column:
                name: city_id
                type: varchar
      - addPrimaryKey:
          columnNames: country_id, city_id
          constraintName: city_cpkey
          tableName: city
      - createTable:
          tableName: district
          columns:
            - column:
                name: id
                type: varchar
                constraints:
                  nullable: false
                  primaryKey: true
            - column:
                name: country_id
                type: varchar
            - column:
                name: city_id
                type: varchar
      - addForeignKeyConstraint:
          baseColumnNames: country_id, city_id
          baseTableName: district
          referencedColumnNames: country_id, city_id
          referencedTableName: city
          constraintName: fk_city_country

问题原因及修复方案

1. CityKey 字段定义错误

City的复合主键是country_id + city_id,但当前CityKey里误写为district_id,导致实体映射和数据库结构不匹配。修正后代码:

@Embeddable
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
@EqualsAndHashCode
public class CityKey implements Serializable {
    @Column(name = "country_id")
    private String countryId;
 
    @Column(name = "city_id")
    private String cityId; // 修正字段名
}

2. CityEntity 关联District的注解错误

多字段关联时,需用@JoinColumns包裹多个@JoinColumn,明确指定关联的复合键字段:

@OneToMany(cascade = {CascadeType.ALL}, fetch = FetchType.EAGER)
@JoinColumns({
    @JoinColumn(name = "country_id", referencedColumnName = "country_id"),
    @JoinColumn(name = "city_id", referencedColumnName = "city_id")
})
private List<DistrictEntity> districts;

3. DistrictEntity 外键关联优化

手动维护country_id和city_id易导致数据不一致,建议用@ManyToOne直接关联City实体:

@Entity
@Table(schema = "public", name = "district")
@NoArgsConstructor
@AllArgsConstructor
@Getter
@Setter
public class DistrictEntity {
    @Id
    public String id;
 
    @ManyToOne
    @JoinColumns({
        @JoinColumn(name = "country_id", referencedColumnName = "country_id"),
        @JoinColumn(name = "city_id", referencedColumnName = "city_id")
    })
    private CityEntity city;
}

4. 插入数据字段顺序验证

错误信息显示(country_id, city_id)=(city01, country01),说明插入时字段顺序颠倒。需确保District的country_id对应国家ID,city_id对应城市ID,与City表复合主键顺序一致。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.15 17:50:43