Getting to know LazyCollectionOption.EXTRA

But of greatest interest is the LazyCollectionOption.EXTRA value. If you specify it as the value of the @LazyCollection annotation , then Hibernate will delay loading the elements of the collection for as long as possible.

If you try to get the number of elements in a collection:

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

Then for all this code, Hibernate will execute only one query:

SELECT COUNT(id) FROM comment WHERE user_id = 1;

However, if you want to get one comment from the collection, for example number 3:

User user = session.load(User.class, 1);
List<Comment> comments = user.getComments();
Comment comment = commetns.get(3);

Then the question arises: how is Hibernate supposed to know that the element is the third without loading all the elements into memory?

To solve this problem, it is proposed to make an additional column in the comment table, which will store the ordinal number of the comment in the collection of comments. And also for this you need a special annotation - @OrderColumn .

Here's what that solution would look like:

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

The main advantage of LazyCollectionOption.EXTRA

We see the strongest advantage of LazyCollectionOption.EXTRA when we specify it with the @ManyToMany annotation . Let's take our old case where we have an Employee, a Task, and we can assign many tasks to one user.

Our Java classes look like this:

Employee class :

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

}

And the EmployeeTask class :

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

}

And to add a task to the director, you need to write something like this code:

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

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

So, if the employees field in the Task class has the LazyCollectionOption.EXTRA annotation, then the employees collection (of the Task class) and the task collection (of the Employee class) will never be loaded from the database at all .

When this code is executed, only one record will be inserted into the employee_task service table, which, as you remember, looks something like this:

employee_task table :
employee_id task_id
1 1
2 2
5 3
5 4
5 5
4 7
6 8
4 101

The added line is highlighted in green. To add this line, you do not need to load collections from the database - Hibernate will do without it. This is exactly the case when LazyCollectionOption.EXTRA greatly speeds up the work with the database.

N+1 problem

But, of course, this mode also has a downside. For example, the LazyCollectionOption.EXTRA annotation generates an N+1 Problem .

If you decide to go through all of your user's comments:

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

Then Hibernate will execute on a separate request for each Comment object. And also one more additional query to get the count of all comments. This can significantly slow down the code.

If your user has 1000 comments, then Hibernate will make 1001 queries to the database to execute this code, although it could do with one. If you knew in advance that you would need all the objects of this class.

undefined
1
Task
Module 4. Working with databases, level 14, lesson 2
Locked
task1403
task1403