Ответ
Класс, чтобы быть Entity в JPA (Java Persistence API), должен соответствовать следующим обязательным требованиям:
- Аннотация
@Entity— обязательная метка класса как сущности. - Пустой конструктор (может быть
protected) — требуется для рефлексии и создания прокси. - Класс не должен быть
final— иначе Hibernate не сможет создать прокси для ленивой загрузки. - Наличие первичного ключа — поле с аннотацией
@Id(может быть составным через@EmbeddedId). - Поля должны быть private/protected с геттерами/сеттерами — для доступа через отражение.
- Методы доступа (геттеры/сеттеры) не должны быть
final— по той же причине, что и класс.
Пример Entity-класса:
@Entity
@Table(name = "users")
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(name = "user_name", nullable = false, length = 100)
private String name;
// Обязательный пустой конструктор
public User() {}
// Геттеры и сеттеры (не final)
public Long getId() { return id; }
public void setId(Long id) { this.id = id; }
public String getName() { return name; }
public void setName(String name) { this.name = name; }
}
Почему эти правила важны:
- Пустой конструктор и не-final класс/методы нужны для работы механизма проксирования Hibernate, который реализует ленивую загрузку (Lazy Loading).
- Приватные поля с геттерами/сеттерами обеспечивают инкапсуляцию и дают Hibernate контролируемый доступ к данным через отражение.
@Idоднозначно идентифицирует запись в базе данных.
Ответ 18+ 🔞
Ну, ты смотри, чтобы твой класс стал полноценной Entity в этой JPA-вотчине, ему надо соблюсти несколько правил, как в строгом монастыре. Не просто так, а чтобы фреймворк не взбесился и не начал кидаться исключениями.
Вот, блядь, свод неписаных, но охуенно важных законов:
- Аннотация
@Entity— это как тавро на жопе. Без неё Hibernate на тебя даже смотреть не станет, будет считать тебя обычным смертным POJO-классом. - Пустой конструктор — да, хоть
protected. Без него рефлексия, эта сука хитрая, не сможет объект собрать на коленке. Для прокси он жизненно важен. - Класс — не
final— а то как же он будет тебе ленивую загрузку через свои прокси-подделки организовывать? Ему же надо от твоего класса наследоваться! Сделаешьfinal— всё, накрылся медным тазом, lazy не будет. - Первичный ключ (
@Id) — это святое! Без уникальной метки, как ты свою запись в таблице потом найдешь? Можно и составной ключ впихнуть (@EmbeddedId), но он должен быть, блядь! - Поля — приватные, доступ — через геттеры/сеттеры — это не прихоть, а вопрос контролируемого доступа. Hibernate через отражение в них лазит, как в своей кладовке.
- Геттеры/сеттеры — тоже не
final— опять же, по той же ебаной причине с проксями. Нельзя их запечатывать.
Вот, смотри, как это выглядит в дикой природе, на примере какого-нибудь User:
@Entity
@Table(name = "users") // Говорим, что в таблице "users" валяться будем
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY) // ID само растёт, как на дрожжах
private Long id;
@Column(name = "user_name", nullable = false, length = 100) // Вот так поле к колонке привяжем
private String name;
// Вот он, священный пустой конструктор! Без него — пиздец.
public User() {}
// И вот эти самые геттеры с сеттерами. Не final, заметь.
public Long getId() { return id; }
public void setId(Long id) { this.id = id; }
public String getName() { return name; }
public void setName(String name) { this.name = name; }
}
А теперь, сука, почему это всё так, а не иначе?
- Пустой конструктор и не-final — это всё ради магии ленивой загрузки (Lazy Loading). Hibernate создаёт прокси-объекты (подставные куклы), которые наследуются от твоего класса. Если класс или методы
final— наследоваться не получится, и вся магия нахуй сломается. Прокси — они хитрожопые, им свобода нужна. - Приватные поля с геттерами — это чтобы не светить кишки направо и налево. Инкапсуляция, мать её! Hibernate через рефлексию аккуратно в сеттер заходит и значение кладёт, а не напрямую в поле плюёт.
@Id— это вообще основа основ. Как в армии — без жетона ты никто. Без первичного ключа запись в базе не идентифицировать, связывать таблицы — нихуя не получится.
Вот и весь сказ, ёпта. Соблюдаешь правила — живёшь в мире и согласии с Hibernate. Нарушаешь — получаешь MappingException в ебало и долгую отладку. Выбор за тобой.