Как реализовать составной первичный ключ в Hibernate?

«Как реализовать составной первичный ключ в Hibernate?» — вопрос из категории Hibernate, который задают на 10% собеседований Java Разработчик. Ниже — развёрнутый ответ с разбором ключевых моментов.

Ответ

В Hibernate есть два основных подхода: @EmbeddedId и @IdClass.

1. Подход с @EmbeddedId (более объектно-ориентированный)

Ключ инкапсулируется в отдельный embeddable-класс.

@Embeddable
public class EmployeeId implements Serializable {
    private String departmentCode;
    private Long employeeNumber;
    // Обязательны: конструктор по умолчанию, equals(), hashCode()
}

@Entity
public class Employee {
    @EmbeddedId
    private EmployeeId id;
    private String name;
    // Доступ к частям ключа: employee.getId().getDepartmentCode()
}

2. Подход с @IdClass

Ключ описывается отдельным классом, но его поля дублируются в entity-классе.

public class ProjectId implements Serializable {
    private String clientId;
    private String projectCode;
    // Обязательны: конструктор по умолчанию, equals(), hashCode()
}

@Entity
@IdClass(ProjectId.class)
public class Project {
    @Id
    private String clientId;
    @Id
    private String projectCode;
    private String description;
    // Ключ создается как new ProjectId(clientId, projectCode)
}

Рекомендации:

  • @EmbeddedId часто предпочтительнее, так как лучше инкапсулирует логику ключа.
  • @IdClass может быть удобнее, если нужен прямой доступ к полям ключа без геттеров (project.clientId).
  • В обоих случаях класс ключа должен быть Serializable и корректно реализовывать equals()/hashCode().