Как смапить составной ключ
Составной ключ состоит из нескольких атрибутов. В JPA есть два основных способа: @EmbeddedId и @IdClass.
Класс ключа должен корректно реализовывать equals и hashCode, быть сериализуемым по требованиям используемой версии JPA и иметь подходящий конструктор.
@EmbeddedId
@Embeddable
public class EnrollmentId implements Serializable {
private Long studentId;
private Long courseId;
// equals и hashCode по обоим полям
}
@Entity
public class Enrollment {
@EmbeddedId
private EnrollmentId id;
}Ключ представлен одним value object. Этот вариант обычно лучше группирует связанные поля.
@IdClass
public class EnrollmentId implements Serializable {
private Long studentId;
private Long courseId;
}
@Entity
@IdClass(EnrollmentId.class)
public class Enrollment {
@Id
private Long studentId;
@Id
private Long courseId;
}Имена и типы полей класса ключа должны соответствовать полям сущности.
Связь как часть ключа
С @EmbeddedId удобно использовать @MapsId, чтобы один и тот же столбец был и внешним, и первичным ключом:
@MapsId("studentId")
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "student_id")
private Student student;Когда лучше surrogate key
Если естественный ключ велик, меняется со временем или проходит через много внешних ключей, технический Long/UUID упрощает связи. Естественную уникальность при этом сохраняют отдельным UNIQUE.
Вопросы для повторения
- Чем
EmbeddedIdотличается отIdClass? В первом ключ — поле-value object, во втором его атрибуты повторены непосредственно в сущности. - Почему важны
equalsиhashCode? Ключ участвует в идентичности сущности и коллекциях. - Можно ли менять первичный ключ managed-сущности? Не следует; идентичность сущности должна быть стабильной.
- Что делает
@MapsId? Связывает атрибут отношения с частью embedded-ключа. - Всегда ли естественный ключ лучше? Нет, изменяемость и ширина могут сделать surrogate key практичнее.
Связано: Встраиваемый класс Embeddable, JoinColumn JoinTable, Аннотация GeneratedValue.