A composite primary key spans more than one column, and Spring Data JPA supports it by defining a separate key class that groups those columns, then linking it to the entity with either @EmbeddedId or @IdClass. @EmbeddedId is the more common and cleaner approach.
Key Points: • The key class must implement Serializable and override equals() and hashCode(), since JPA uses these to compare identity. • @Embeddable marks the composite key class so it can be embedded into the entity. • @EmbeddedId is placed on a single field in the entity of the embeddable key type, representing the whole composite key as one object. • @IdClass is an alternative where the individual key fields live directly on the entity, and a separate class mirrors their names for JPA to use as the identifier. • Repository interfaces for entities with a composite key use the key class as the ID type parameter, e.g. JpaRepository<Enrollment, EnrollmentId>.
Example: An Enrollment entity linking a student and a course has no single natural primary key, so its uniqueness is defined by the combination of studentId and courseId, modeled as an EnrollmentId class annotated with @Embeddable and referenced from Enrollment via @EmbeddedId.
Code Example:
@Embeddable
public class EnrollmentId implements Serializable {
private Long studentId;
private Long courseId;
// equals(), hashCode(), no-arg constructor
}
@Entity
public class Enrollment {
@EmbeddedId
private EnrollmentId id;
private LocalDate enrolledOn;
}
public interface EnrollmentRepository extends JpaRepository<Enrollment, EnrollmentId> {
}Interview Tip: A concise interview answer is:
"I define a separate @Embeddable class holding the composite key's fields, implementing Serializable with proper equals() and hashCode(), and then reference it from the entity using @EmbeddedId. The repository then uses that key class as its ID type, like JpaRepository<Enrollment, EnrollmentId>."