要映射组合键,你可以使用EmbeddedId 或在IdClass注解。我知道这个问题不仅仅涉及JPA,但规范定义的规则也适用。因此,它们是:
2.1.4主键和实体身份
…
组合主键必须对应于单个持久性字段或属性,或对应于如下所述的一组此类字段或属性。必须定义一个主键类来表示一个复合主键。当数据库密钥由几列组成时,从传统数据库进行映射时,通常会出现复合主键。的EmbeddedId和 IdClass注解用于表示复合主键。参见9.1.14和9.1.15节。
…
以下规则适用于复合主键:
- 主键类必须是公共的,并且必须具有公共的无参数构造函数。
- 如果使用基于属性的访问,则主键类的属性必须是公共的或受保护的。
- 主键类必须为
serializable
。 - 主键类必须定义
equals
和hashCode
方法。这些方法的值相等的语义必须与键映射到的数据库类型的数据库相等一致。 - 复合主键必须表示并映射为可嵌入类(请参见第9.1.14节“ EmbeddedId注释”),或者必须表示并映射至实体类的多个字段或属性(请参见第9.1.15节“ IdClass”)注解”)。
- 如果组合主键类映射到实体类的多个字段或属性,则主键类中的主键字段或属性名称与实体类的名称必须对应,并且它们的类型必须相同。
With an IdClass
复合主键的类可能看起来像(可以是静态内部类):
public class TimePK implements Serializable { protected Integer levelStation; protected Integer confPathID; public TimePK() {} public TimePK(Integer levelStation, Integer confPathID) { this.levelStation = levelStation; this.confPathID = confPathID; } // equals, hashCode}And the entity:
@Entity@IdClass(TimePK.class)class Time implements Serializable { @Id private Integer levelStation; @Id private Integer confPathID; private String src; private String dst; private Integer distance; private Integer price; // getters, setters}该IdClass注释映射多个字段的表PK。
With EmbeddedId
复合主键的类可能看起来像(可以是静态内部类):
@Embeddablepublic class TimePK implements Serializable { protected Integer levelStation; protected Integer confPathID; public TimePK() {} public TimePK(Integer levelStation, Integer confPathID) { this.levelStation = levelStation; this.confPathID = confPathID; } // equals, hashCode}和实体:
@Entityclass Time implements Serializable { @EmbeddedId private TimePK timePK; private String src; private String dst; private Integer distance; private Integer price; //...}该
@EmbeddedId注解映射一个PK类表PK。
差异:
- 从物理模型的角度来看,没有区别
- @EmbeddedId通过某种方式可以更清楚地传达出该密钥是组合密钥,而当组合的pk本身是有意义的实体或在代码中重用时,IMO便有意义。
- @IdClass 用来指定某些字段组合是唯一的,但这些字段没有特殊含义。
它们还会影响你编写查询的方式(使它们或多或少变得冗长):
with IdClass
select t.levelStation from Time t
with EmbeddedId
select t.timePK.levelStation from Time t



