Запознаване с LazyCollectionOption.EXTRA

Но най-голям интерес представлява стойността LazyCollectionOption.EXTRA. Ако го посочите като стойност на анотацията @LazyCollection , тогава Hibernate ще забави зареждането на елементите на колекцията възможно най-дълго.

Ако се опитате да получите броя на елементите в колекция:

User user = session.load(User.class, 1);
List<Comment> comments = user.getComments();
int count = commetns.size();

Тогава за целия този code 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 = commetns.get(3);

Тогава възниква въпросът: How се предполага, че Hibernate знае, че елементът е третият, без да зареди всички елементи в паметта?

За да се реши този проблем, се предлага да се направи допълнителна колона в tableта с коментари, която да съхранява поредния номер на коментара в колекцията от коментари. И също така за това се нуждаете от специална анотация - @OrderColumn .

Ето How би изглеждало това решение:

@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;
}

Основното предимство на LazyCollectionOption.EXTRA

Виждаме най-силното предимство на LazyCollectionOption.EXTRA, когато го посочим с анотацията @ManyToMany . Нека вземем нашия стар случай, в който имаме служител, задача и можем да възлагаме много задачи на един потребител.

Нашите Java класове изглеждат така:

Клас служител :

@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>();

}

И за да добавите задача към директора, трябва да напишете нещо като този code:

Employee director = session.find(Employee.class, 4);
EmployeeTask task = session.find(EmployeeTask.class, 101);
task.employees.add(director);

session.update(task);
session.flush();

Така че, ако полето за служители в класа Task има анотацията LazyCollectionOption.EXTRA, тогава колекцията за служители (от класа Task) и колекцията от задачи (от класа Employee) изобщо няма да бъдат заредени от базата данни .

Когато този code се изпълни, само един запис ще бъде вмъкнат в служебната table employee_task, която, Howто си спомняте, изглежда по следния начин:

table за_задачи на служител :
ИД на служител task_id
1 1
2 2
5 3
5 4
5 5
4 7
6 8
4 101

Добавената линия се маркира в зелено. За да добавите този ред, не е необходимо да зареждате колекции от базата данни - Hibernate ще направи без него. Това е точно случаят, когато LazyCollectionOption.EXTRA значително ускорява работата с базата данни.

N+1 проблем

Но, разбира се, този режим има и недостатък. Например анотацията LazyCollectionOption.EXTRA генерира N+1 проблем .

Ако решите да прегледате всички коментари на вашия потребител:

User user = session.load(User.class, 1);
List<Comment> comments = user.getComments();
for (Comment comment : comments) {
    System.out.println(comment);
}

Тогава Hibernate ще се изпълни по отделна заявка за всеки обект на коментар. И още една допълнителна заявка, за да получите броя на всички коментари. Това може значително да забави codeа.

Ако вашият потребител има 1000 коментара, тогава Hibernate ще направи 1001 заявки към базата данни, за да изпълни този code, въпреки че може да се справи и с един. Ако знаехте предварително, че ще имате нужда от всички обекти от този клас.