Ответ
Требования к классу-сущности в Hibernate совпадают со спецификацией JPA и включают:
Обязательные условия:
- Аннотация
@Entity. - Наличие поля с
@Id(первичный ключ). - Пустой конструктор (доступ
publicилиprotected). - Класс не должен быть объявлен как
final. - Методы-аксессоры (геттеры/сеттеры) для персистентных полей не должны быть
final.
Рекомендации и best practices:
- Поля обычно объявляются как
private. - Реализуйте методы
equals()иhashCode(), основанные на бизнес-ключе (уникальном наборе полей, кроме@Id), а не на сгенерированном ID, так как ID присваивается после сохранения. - Используйте аннотации для маппинга:
@Table,@Column,@OneToMany,@ManyToOneи т.д.
Пример сущности с бизнес-ключом:
@Entity
@Table(name = "products")
public class Product {
@Id
@GeneratedValue(strategy = GenerationType.SEQUENCE)
private Long id;
@Column(unique = true, nullable = false)
private String sku; // Часть бизнес-ключа
private String name;
public Product() {}
// equals и hashCode на основе sku (бизнес-ключ)
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Product product = (Product) o;
return Objects.equals(sku, product.sku);
}
@Override
public int hashCode() {
return Objects.hash(sku);
}
// Геттеры и сеттеры...
}
Почему важен бизнес-ключ для equals/hashCode?
Пока объект не сохранен в БД, его id равен null. Сравнение по id сломает контракт этих методов. Сравнение по неизменяемому уникальному полю (например, артикулу sku) решает эту проблему.
Ответ 18+ 🔞
Давай разберём эту хрень с сущностями в Hibernate, а то народ тут как будто в первый раз на лекцию по джаве пришёл, ей-богу.
Смотри, чтобы твой класс стал полноценной сущностью, от которой Hibernate не сломается в припадке, нужно соблюсти несколько простых, но обязательных правил. Не сделаешь — получишь MappingException прямо в ебало, и будешь потом гадать, че не так.
Что ОБЯЗАТЕЛЬНО нужно, блядь:
@Entityсверху класса. Без этой аннотации Hibernate на твой класс посмотрит как баран на новые ворота и проигнорирует нахуй. Это как паспорт для таблицы в БД.- Хотя бы одно поле с
@Id. Это первичный ключ, чувак. Без него — пиздец и анархия, база данных просто не поймёт, как отличать одну запись от другой. Это как лицо у человека, только для строки в таблице. - Пустой конструктор.
publicилиprotected— не важно, главное, чтобы он был. Hibernate сам создаёт объекты через рефлексию, и ему нужен этот конструктор, чтобы не еб*ться. Если его нет — готовься кNoSuchMethodException. - Класс НЕ должен быть
final. Hibernate любит создавать прокси-объекты (для ленивой загрузки, например), а для этого он наследуется от твоего класса. Если классfinal— наследоваться не получится, и Hibernate обидится, как сука. - Геттеры и сеттеры НЕ должны быть
final. По той же самой причине — для прокси он их может подменять своей магией. Сделаешьfinal— магия не сработает.
А теперь про то, как делать НЕ как последний лузер:
- Поля, как правило, делают
private. Это просто хороший тон, хотя Hibernate может и в поля напрямую лезть, если ему разрешить. - Вот это важно, блядь, слушай сюда! Все эти
equals()иhashCode()— реализуй их на основе бизнес-ключа, а не на сгенерированномid! Почему? Да потому чтоidу нового, ещё не сохранённого объекта —null, ёпта! Представь, добавляешь ты два одинаковых продукта вHashSet, а у нихid = null. По логикеequals()они будут равны? Да нихуя! Контракт методов сломается, и будет тебе больно и неприятно. Бизнес-ключ — это какое-то уникальное поле (или комбинация полей) в рамках твоей логики, типа артикула (sku) или email пользователя.
Смотри, как это выглядит в коде, чтобы не быть мудаком:
@Entity
@Table(name = "products") // Говорим, в какой таблице храниться
public class Product {
@Id
@GeneratedValue(strategy = GenerationType.SEQUENCE) // ID генерится базой
private Long id; // Пока не сохранил — id = null, помни это, кретин!
@Column(unique = true, nullable = false) // Артикул уникальный и обязательный
private String sku; // Вот он, наш спаситель — бизнес-ключ!
private String name;
public Product() {} // Обязательный пустой конструктор, блядь!
// equals и hashCode на основе sku, а не на основе ебучего id!
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Product product = (Product) o;
return Objects.equals(sku, product.sku); // Сравниваем по артикулу!
}
@Override
public int hashCode() {
return Objects.hash(sku); // Хэш тоже по артикулу!
}
// Дальше идут стандартные геттеры/сеттеры, их я тут за тебя писать не буду, не маленький...
}
Итог, блядь: Сделал @Entity, @Id, пустой конструктор, не final класс — уже молодец. Но если ещё и equals/hashCode на бизнес-ключе напишешь — будешь вообще красавчик, и коллекции у тебя не поедут в разнос. Всё просто, как три копейки.