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
.