To map a composite key, you can use the EmbeddedId
or the IdClass
annotations. I know this question is not strictly about JPA but the rules defined by the specification also applies. So here they are:
2.1.4 Primary Keys and Entity Identity
...
A composite primary key must
correspond to either a single
persistent field or property or to a
set of such fields or properties as
described below. A primary key class
must be defined to represent a
composite primary key. Composite
primary keys typically arise when
mapping from legacy databases when the
database key is comprised of several
columns. The EmbeddedId
and
IdClass
annotations are used to
denote composite primary keys. See
sections 9.1.14 and 9.1.15.
...
The following rules apply for
composite primary keys:
- The primary key class must be public and must have a public no-arg
constructor.
- If property-based access is used, the properties of the primary key
class must be public or protected.
- The primary key class must be
serializable
.
- The primary key class
must define
equals
and hashCode
methods. The semantics of value
equality for these methods must be
consistent with the database equality
for the database types to which the
key is mapped.
- A composite primary key must either be represented and mapped as an
embeddable class (see Section 9.1.14,
“EmbeddedId Annotation”) or must be
represented and mapped to multiple
fields or properties of the entity
class (see Section 9.1.15, “IdClass
Annotation”).
- If the composite primary key class is mapped to multiple fields or
properties of the entity class, the
names of primary key fields or
properties in the primary key class
and those of the entity class must
correspond and their types must be the
same.
With an IdClass
The class for the composite primary key could look like (could be a static inner class):
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
}
The IdClass
annotation maps multiple fields to the table PK.
With EmbeddedId
The class for the composite primary key could look like (could be a static inner class):
@Embeddable
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
class Time implements Serializable {
@EmbeddedId
private TimePK timePK;
private String src;
private String dst;
private Integer distance;
private Integer price;
//...
}
The @EmbeddedId
annotation maps a PK class to table PK.
Differences:
- From the physical model point of view, there are no differences
@EmbeddedId
somehow communicates more clearly that the key is a composite key and IMO makes sense when the combined pk is either a meaningful entity itself or it reused in your code.
@IdClass
is useful to specify that some combination of fields is unique but these do not have a special meaning.
They also affect the way you write queries (making them more or less verbose):
References
- JPA 1.0 specification
- Section 2.1.4 "Primary Keys and Entity Identity"
- Section 9.1.14 "EmbeddedId Annotation"
- Section 9.1.15 "IdClass Annotation"
与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…