Gymterview
middle

Как маппить связи между сущностями? В чём разница между LAZY и EAGER загрузкой?

JPA поддерживает четыре типа связей: @ManyToOne, @OneToMany, @ManyToMany, @OneToOne.

Значения по умолчанию (важно знать!)

Аннотация FetchType по умолчанию
@ManyToOne EAGER (это плохо!)
@OneToOne EAGER (это плохо!)
@OneToMany LAZY (это хорошо)
@ManyToMany LAZY (это хорошо)

Рекомендация: всегда явно указывайте fetch = FetchType.LAZY для всех связей.

@ManyToOne / @OneToMany

Пример
@Entity
public class Order {
    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "user_id", nullable = false)
    private User user;
}

@Entity
public class User {
    @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);
    }
}

LAZY vs EAGER

Тип Когда загружается Когда использовать
LAZY При первом обращении к коллекции/полю По умолчанию для всех связей
EAGER Сразу вместе с родительской сущностью Почти никогда

CascadeType

CascadeType Описание
PERSIST Каскадное сохранение
MERGE Каскадное обновление
REMOVE Каскадное удаление
ALL Все вышеперечисленные

На собеседовании: ключевое – знать значения FetchType по умолчанию и всегда ставить LAZY. Частая ошибка – оставлять EAGER по умолчанию для @ManyToOne (причина проблемы N+1) или использовать CascadeType.ALL без необходимости.