middle
Маппинг связей между сущностями
Hibernate поддерживает четыре типа связей, соответствующих отношениям в реляционной модели.
@ManyToOne / @OneToMany — самая частая связь
Пример двунаправленной связи
// Сторона "многие" — владелец связи (содержит FK)
@Entity
public class Order {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "user_id", nullable = false)
private User user;
}
// Сторона "один" — обратная сторона
@Entity
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@OneToMany(mappedBy = "user", cascade = CascadeType.ALL, orphanRemoval = true)
private List<Order> orders = new ArrayList<>();
// Вспомогательные методы для синхронизации обеих сторон
public void addOrder(Order order) {
orders.add(order);
order.setUser(this);
}
public void removeOrder(Order order) {
orders.remove(order);
order.setUser(null);
}
}
@OneToOne
Пример
@Entity
public class User {
@OneToOne(mappedBy = "user", cascade = CascadeType.ALL,
fetch = FetchType.LAZY, optional = false)
private UserProfile profile;
}
@Entity
public class UserProfile {
@Id
private Long id; // общий PK с User
@OneToOne(fetch = FetchType.LAZY)
@MapsId // PK UserProfile = FK на User
@JoinColumn(name = "id")
private User user;
}
@ManyToMany
Пример
@Entity
public class Student {
@ManyToMany
@JoinTable(
name = "student_course",
joinColumns = @JoinColumn(name = "student_id"),
inverseJoinColumns = @JoinColumn(name = "course_id"))
private Set<Course> courses = new HashSet<>();
}
@Entity
public class Course {
@ManyToMany(mappedBy = "courses")
private Set<Student> students = new HashSet<>();
}
Важное
mappedBy— указывает обратную (не владеющую) сторону связи; FK хранится на стороне без mappedBy- Всегда используйте
FetchType.LAZYдля@OneToManyи@ManyToMany @ManyToOneпо умолчанию EAGER — явно указывайте LAZY- Для
@ManyToManyпредпочитайте Set вместо List — производительнее при операциях удаления - Синхронизируйте обе стороны связи (helper-методы addOrder, removeOrder)
Частые ошибки
- Не указывать mappedBy — Hibernate создаст промежуточную таблицу вместо использования FK
- Использовать List в
@ManyToMany— при удалении Hibernate удалит все записи из join-таблицы и вставит заново; Set удаляет точечно - EAGER для коллекций — загрузка всех связанных сущностей сразу ведёт к N+1 и OutOfMemoryError
- Двунаправленная связь без синхронизации — если не обновить обе стороны, in-memory модель не совпадает с БД
@OneToOneс LAZY — LAZY для@OneToOneна стороне без FK не работает без bytecode enhancement (Hibernate должен знать, null ли связь, что требует запроса)
Как используется в 2026
@ManyToOne(fetch = LAZY)+ JOIN FETCH — золотой стандарт@ManyToManyчасто заменяется промежуточной сущностью (@Entity StudentCourse) для добавления атрибутов связи- Hibernate 6 улучшил работу с
@OneToOneLAZY через bytecode enhancement
На собеседовании: начните с
@ManyToOne/@OneToManyкак самой частой связи. Объясните mappedBy — кто владелец связи и где FK. Ключевое правило — всегда LAZY. Интервьюер может спросить про подводные камни@ManyToMany— ответ: используйте Set, а лучше промежуточную сущность.