1. Знайомимося з LazyCollectionOption.EXTRA
Але найбільший інтерес являє собою значення LazyCollectionOption.EXTRA. Якщо ти вказуєш його як значення анотації @LazyCollection, то Hibernate відтягуватиме завантаження елементів колекції якомога довше.
Якщо ти спробуєш отримати кількість елементів колекції:
User user = session.load(User.class, 1);
List<Comment> comments = user.getComments();
int count = comments.size();
То для всього цього коду Hibernate виконає лише один запит:
SELECT COUNT(id) FROM comment WHERE user_id=1;
Але якщо ти хочеш отримати один коментар із колекції, наприклад, номер 3:
User user = session.load(User.class, 1);
List<Comment> comments = user.getComments();
Comment comment = comments.get(3);
То виникне питання: а як Hibernate повинен дізнатися, що елемент саме третій, не завантажуючи на згадку всі елементи?
Для вирішення цієї проблеми пропонується зробити в таблиці comment додаткову колонку, яка зберігатиме порядковий номер коментаря в колекції коментарів. Також для цього потрібна спеціальна анотація — @OrderColumn.
Ось як виглядатиме це рішення:
@Entity
@Table(name="user")
class User {
@Column(name=”id”)
public Integer id;
@OneToMany(cascade = CascadeType.ALL)
@LazyCollection(LazyCollectionOption.EXTRA)
@OrderColumn(name = "order_id")
public List<Comment> comments;
}
2. Головна перевага LazyCollectionOption.EXTRA
Найбільшу перевагу LazyCollectionOption.EXTRA ми можемо спостерігати, коли вказуємо її в анотації @ManyToMany. Візьмемо наш старий випадок, коли ми маємо Employee, Task, і можна призначати багато завдань на одного користувача.
Наші Java-класи виглядають так:
Клас Employee:
@Entity
@Table(name=”employee”)
class Employee {
@Column(name=”id”)
public Integer id;
@ManyToMany(cascade = CascadeType.ALL)
@JoinTable(name="employee_task",
joinColumns= @JoinColumn(name="employee_id", referencedColumnName="id"),
inverseJoinColumns= @JoinColumn(name="task_id", referencedColumnName="id") )
@LazyCollection(LazyCollectionOption.EXTRA)
private Set<EmployeeTask> tasks = new HashSet<EmployeeTask>();
}
І клас EmployeeTask:
@Entity
@Table(name=”task”)
class EmployeeTask {
@Column(name=”id”)
public Integer id;
@ManyToMany(cascade = CascadeType.ALL)
@JoinTable(name="employee_task",
joinColumns= @JoinColumn(name="task_id", referencedColumnName="id"),
inverseJoinColumns= @JoinColumn(name="employee_id", referencedColumnName="id") )
@LazyCollection(LazyCollectionOption.EXTRA)
private Set<Employee> employees = new HashSet<Employee>();
}
А щоб додати завдання директорові, потрібно написати приблизно такий код:
Employee director = session.find(Employee.class, 4);
EmployeeTask task = session.find(EmployeeTask.class, 101);
task.employees.add(director);
session.update(task);
session.flush();
Так ось, якщо поле employees у класі Task має анотацію LazyCollectionOption.EXTRA, колекції employees (у класу Task) та колекції task (у класу Employee) взагалі не будуть завантажені з бази жодного разу.
Під час виконання цього коду буде вставлено лише один запис до службової таблиці employee_task, яка, як ти пам'ятаєш, виглядає приблизно так:
Таблиця employee_task:employee_id | task_id |
---|---|
1 | 1 |
2 | 2 |
5 | 3 |
5 | 4 |
5 | 5 |
4 | 7 |
6 | 8 |
4 | 101 |
Зеленим кольором виділено доданий рядок. Щоб додати цей рядок, не потрібно завантажувати колекції з бази — Hibernate обійдеться без цього. Це саме той випадок, коли LazyCollectionOption.EXTRA дуже прискорює роботу з базою.
3. N+1 Problem
Але, звісно, має цей режим і зворотний бік. Так, анотація LazyCollectionOption.EXTRA породжує проблему N+1 Problem.
Якщо ти вирішиш пройтися всіма коментарями свого користувача:
User user = session.load(User.class, 1);
List<Comment> comments = user.getComments();
for (Comment comment : comments) {
System.out.println(comment);
}
То Hibernate буде виконувати за окремим запитом для кожного об'єкта Comment. А також ще один додатковий запит, щоб отримати кількість усіх коментарів. Це може суттєво уповільнити роботу коду.
Якщо у твого користувача 1000 коментарів, то для виконання цього коду Hibernate зробить 1001 запит до бази, хоча міг би обійтися й одним. Якби знав, що тобі знадобляться всі об'єкти цього класу.
ПЕРЕЙДІТЬ В ПОВНУ ВЕРСІЮ