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

如何在JPA中映射复合外键与复合主键的关联关系

问题:JPA映射复合主键+复合外键的一对多关系

现有client和device两张表,为一对多关系(一个client对应多个device),简化表结构如下:

TABLE client (
  id,
  import_date,
  PRIMARY_KEY (id, import_date)
)

以及

TABLE device (
  id,
  import_date,
  client_id,
  PRIMARY_KEY (id, import_date),
  FOREIGN_KEY (client_id -> client.id, import_date -> client.import_date)
)

注意:device.import_date列既是主键的一部分,也是外键的一部分。

我的尝试

我无法用JPA正确描述上述结构,以下是我的尝试(省略getter、setter及实现的接口等):

public class ImportId {
    private Integer id;
    private LocalDate importDate;
}

@Entity
@IdClass(ImportId.class)
public class Client {
    @Id
    private Integer id;
    @Id
    private LocalDate importDate;
}


@Entity
@IdClass(ImportId.class)
public class Device {
    @Id
    private Integer id;
    @Id
    private LocalDate importDate;
    @ManyToOne
    //@MapsId("importDate") <-- 无效
    //@JoinColumn
    //@JoinColumn           <-- 同样无效
    private Client client;
}

将@MapsId与@IdClass配合使用未达预期,会在device表中生成多余的client_import_date列,不符合需求。

我还尝试将@MapsId与@Embeddable配合使用,但抛出异常:

org.hibernate.PropertyNotFoundException: Could not locate field name [id] on class [java.time.LocalDate]

求解决办法,谢谢!


更新1(针对Phil Ku的方案)

初看该方案可行,Hibernate不会创建client_import_date列,外键设置也正确,但无法保存device实体。执行以下代码:

// var client = clientRepository.save(new Client(1234, LocalDate.now());
deviceRepository.save(new Device(567, LocalDate.now(), client));

Hibernate生成的SQL:

INSERT INTO device(client_id, import_date, id) VALUES(?,?,?);

随后尝试绑定的参数:

[1] as [INTEGER] - [1234]
[2] as [DATE] - [2023-03-22]
[3] as [INTEGER] - [567]
[4] as [DATE] - [2023-03-22]

抛出异常Invalid value "4" for parameter "parameterIndex",显然会失败,因为SQL只有3个参数位。有人知道为什么Hibernate参数列表和插入语句不匹配吗?

更新1.1

上述Hibernate异常似乎仅在H2平台出现,可能是集成测试的问题。测试版本:Hibernate 6.1.7和H2 2.1.214,均随Spring Boot 3.0.4提供。

使用MariaDB驱动时,Phil Ku的方案运行正常。追踪Hibernate输出发现仍有四个候选参数,但选择前三个即可正常执行。


更新2

接下来遇到事务相关问题。执行以下代码:

var importDate = LocalDate.now();
var client = clientRepository.save(new Client(100, importDate));
var device1 = new Device(1, importDate, client);
var device2 = new Device(2, importDate, client);
deviceRepository.saveAll(List.of(device1, device2));

抛出异常:

SQLIntegrityConstraintViolationException: Cannot add or update a child row: a foreign key constraint fails (`device`, CONSTRAINT `...` FOREIGN KEY (`client_id`, `import_date`) REFERENCES `client` (`id`, `import_date`))

但如果改为逐个调用deviceRepository.save保存每个设备,一切正常。

我无法在JPA/Hibernate上下文之外复现此行为,直接在数据库执行对应的INSERT语句——无论单事务还是多事务——都不会触发约束违反。

对此有什么思路吗?


更新3

基于K.Nicholas和Phil Ku的建议,我重新设计了数据库结构:client表使用普通的单列数值主键,import_date列仅作为与id列组成的唯一约束的一部分,主键随后被device表引用。

这算是个变通方案,但运行完全正常。

感谢大家的帮助。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.27 05:29:58