Ответ
Hibernate использует разные стратегии загрузки по умолчанию для разных типов ассоциаций, чтобы сбалансировать производительность и удобство.
Правила по умолчанию:
FetchType.EAGER(жадная загрузка): Для@ManyToOneи@OneToOne.FetchType.LAZY(ленивая загрузка): Для@OneToManyи@ManyToMany.
Пример сущности:
@Entity
public class Order {
@Id
private Long id;
// Загружается сразу вместе с Order (EAGER)
@ManyToOne
private Customer customer;
// Загружается только при обращении к полю (LAZY)
@OneToMany(mappedBy = "order")
private List<Item> items = new ArrayList<>();
}
Почему такие настройки?
EAGERдля@ManyToOne: Обычно «многие» заказы ссылаются на одного клиента. Загрузка клиента сразу часто нужна и эффективна.LAZYдля@OneToMany: У одного заказа может быть много позиций. Их жадная загрузка может привести кN+1проблеме или избыточному объему данных. Ленивая загрузка позволяет загружать коллекцию только когда она действительно требуется.
Важно: Рекомендуется явно указывать FetchType.LAZY для всех ассоциаций, кроме критически необходимых, и использовать JOIN FETCH в запросах для контролируемой загрузки.
Ответ 18+ 🔞
А, слушай, вот эта тема с Hibernate и его стратегиями загрузки — это просто ёперный театр, если честно. Ну, типа, он там сам по себе, этот хибер, решает, что ему грузить сразу, а что — потом, когда спросят. И у него там свои, блядь, правила по умолчанию, которые вроде как для нашего же удобства, но иногда от них волосы дыбом, ебать мои старые костыли.
Вот смотри, какие у него приколы:
Как он по дефолту работает, этот хитрый жоп:
FetchType.EAGER(жадная загрузка): Включается автоматом для связей@ManyToOneи@OneToOne. То есть, грузит всё сразу, не стесняясь.FetchType.LAZY(ленивая загрузка): Сам включается для@OneToManyи@ManyToMany. То есть, будет тянуть данные только когда ты к ним полезешь, хитрая жопа.
Вот тебе пример, чтобы не быть голословным:
@Entity
public class Order {
@Id
private Long id;
// Этот клиент загрузится сразу, вместе с заказом (EAGER)
@ManyToOne
private Customer customer;
// А вот эта куча позиций в заказе — нет. Загрузится только если спросишь (LAZY)
@OneToMany(mappedBy = "order")
private List<Item> items = new ArrayList<>();
}
А теперь, блядь, почему так, а не иначе?
EAGERдля@ManyToOne: Ну, логично же, вроде. «Много» заказов обычно ссылаются на «одного» клиента. И почти всегда, когда ты заказ смотришь, тебе и клиента его надо. Вот хибер и думает: «А зачем два раза ходить? Сразу всё принесу». Вроде бы эффективно, да.LAZYдля@OneToMany: А вот тут уже, блядь, подстава может быть. У одного заказа позиций может быть овердохуища. Представь, что ты грузишь список из 100 заказов, а у каждого по 50 позиций. Если всё это потянуть жадно, получишь 5000 записей разом, и сервер тебе скажет «иди нахуй» красиво и без мата. Поэтому умный Hibernate делает вид, что коллекции нет, пока ты её не тронешь. Лениво, зато безопасно.
Но вот что я тебе скажу, как человек, который уже на этих граблях плясал: лучшая практика — это явно везде, где только можно, ставить FetchType.LAZY. Да-да, даже на тех @ManyToOne, где он по умолчанию жадный. Потому что потом, когда тебе реально понадобится эта связь в конкретном запросе, ты её явно подтянешь через JOIN FETCH в JPQL. Так ты контролируешь, что и когда грузится, а не надеешься на авось и дефолтные настройки этой, с позволения сказать, мартышлюшки. Иначе однажды проснёшься с проблемой N+1 запросов, и будешь думать: «Ну я же, блядь, мудак... что я натворил?».