1. Параметр fetch

Розробники Hibernate давно знали про проблему, пов'язану із завантаженням дочірніх сутностей. Отже, перше, що вони зробили — додали спеціальний параметр fetch до анотацій @OneToMany, @ManyToMany.

Цей параметр може приймати два значення:

  • EAGER
  • LAZY

Приклад:

@OneToMany(fetch = FetchType.LAZY, mappedBy = "user")

Якщо параметр fetch дорівнює EAGER, то при завантаженні батьківської сутності будуть завантажені і всі її дочірні сутності. Крім того, Hibernate намагатиметься зробити це одним SQL-запитом, згенерувавши великий запит і одразу отримавши всі дані.

Якщо параметр fetch приймає значення LAZY, то при завантаженні батьківської сутності дочірня сутністьа не буде завантажен. Замість неї буде створено proxy-об'єкт.

За допомогою цього proxy-об'єкта Hibernate відстежуватиме звернення до цієї дочірньої сутності, і під час першого звернення завантажить її до пам'яті.

Якщо згадати нашу ситуацію з коментарями:


@Entity
@Table(name="user")
class User {
   @Column(name="id")
   public Integer id;
 
   @OneToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY)
   @JoinColumn(name="user_id")
   public List<Comment> comments;
}

То маєш “шикарний вибір”:

Якщо fetch = FetchType.EAGER, то Hibernate завантажить усі коментарі на першому рядку коду:


User user = session.get(User.class, 1); //завантажить всі коментарі тут
List<Comment> comments = user.getComments();

Якщо fetch = FetchType.LAZY, то Hibernate завантажить усі коментарі на другому рядку коду:


User user = session.get(User.class, 1);
List<Comment> comments = user.getComments(); //завантажить всі коментарі тут

Як ти вже здогадуєшся, варіанта, коли він не завантажить всі коментарі, у тебе немає :)

2. Значення за умовчанням

Якщо ти не вказав параметр fetch для анотації @ManyTo…, то Hibernate використовуватиме значення за замовчуванням.

Вони дещо відрізняються для різних типів анотацій. Для анотацій @OneToOne та @ManyToOne це EAGER, для анотацій @OneToMany та @ManyToMany — LAZY. Запам'ятати просто: якщо ми посилаємось на один об'єкт, то він буде завантажений повністю. Якщо посилаємось на колекцію, вона буде завантажена під час першого звернення до неї.

3. Анотація @LazyCollection

Як ти вже переконався, параметр fetch не дуже допомагає під час роботи з колекціями. Творці Hibernate спробували це виправити, за допомогою додавання спеціальної анотації @LazyCollection. Пишеться вона зазвичай так:

@LazyCollection(LazyCollectionOption.TRUE)

Вказувати її потрібно під час мапінгу полів-колекцій:


@Entity
@Table(name="user")
class User {
   @Column(name="id")
   public Integer id;
 
   @OneToMany(cascade = CascadeType.ALL)
   @LazyCollection(LazyCollectionOption.TRUE)
   public List<Comment> comments;
}

Ця анотація має параметр value, який може набувати одного з трьох значень:

  • LazyCollectionOption.TRUE
  • LazyCollectionOption.FALSE
  • LazyCollectionOption.EXTRA

Перші два варіанти дуже схожі на параметр fetch.

Якщо параметр встановлено в LazyCollectionOption.TRUE, це означає, що значення поля comments не будуть завантажені з бази в момент завантаження батьківського об'єкта User. Об'єкти типу Comment будуть завантажені під час першого звернення до поля comments. Фактично це еквівалент параметра FetchType.LAZY

Якщо параметр встановлений у LazyCollectionOption.FALSE, це означає, що значення поля comments будуть завантажені з бази в момент завантаження батьківського об'єкта User. Об'єкти типу Comment будуть завантажені під час першого звернення до поля comments. Фактично це еквівалент параметра FetchType.EAGER.