Apprendre à connaître LazyCollectionOption.EXTRA

Mais le plus intéressant est la valeur LazyCollectionOption.EXTRA. Si vous le spécifiez comme valeur de l' annotation @LazyCollection , alors Hibernate retardera le chargement des éléments de la collection aussi longtemps que possible.

Si vous essayez d'obtenir le nombre d'éléments dans une collection :

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

Alors pour tout ce code, Hibernate n'exécutera qu'une seule requête :

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

Cependant, si vous souhaitez obtenir un commentaire de la collection, par exemple le numéro 3 :

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

Alors la question se pose : comment Hibernate est-il supposé savoir que l'élément est le troisième sans charger tous les éléments en mémoire ?

Pour résoudre ce problème, il est proposé de créer une colonne supplémentaire dans la table de commentaires, qui stockera le numéro ordinal du commentaire dans la collection de commentaires. Et aussi pour cela, vous avez besoin d'une annotation spéciale - @OrderColumn .

Voici à quoi ressemblerait cette solution :

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

Le principal avantage de LazyCollectionOption.EXTRA

Nous voyons le plus grand avantage de LazyCollectionOption.EXTRA lorsque nous le spécifions avec l' annotation @ManyToMany . Prenons notre ancien cas où nous avons un Employé, une Tâche, et nous pouvons assigner plusieurs tâches à un seul utilisateur.

Nos classes Java ressemblent à ceci :

Classe d'employés :

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

}

Et la classe 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>();

}

Et pour ajouter une tâche au directeur, vous devez écrire quelque chose comme ce code :

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

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

Ainsi, si le champ des employés de la classe Task a l'annotation LazyCollectionOption.EXTRA, la collection des employés (de la classe Task) et la collection des tâches (de la classe Employee) ne seront jamais chargées à partir de la base de données .

Lorsque ce code est exécuté, un seul enregistrement sera inséré dans la table de service employee_task, qui, comme vous vous en souvenez, ressemble à ceci :

table employee_task :
id_employé id_tâche
1 1
2 2
5 3
5 4
5 5
4 7
6 8
4 101

La ligne ajoutée est surlignée en vert. Pour ajouter cette ligne, vous n'avez pas besoin de charger des collections depuis la base de données - Hibernate s'en passera. C'est exactement le cas lorsque LazyCollectionOption.EXTRA accélère considérablement le travail avec la base de données.

Problème N+1

Mais, bien sûr, ce mode a aussi un inconvénient. Par exemple, l'annotation LazyCollectionOption.EXTRA génère un N+1 Problem .

Si vous décidez de passer en revue tous les commentaires de votre utilisateur :

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

Ensuite, Hibernate s'exécutera sur une requête distincte pour chaque objet Comment. Et aussi une autre requête supplémentaire pour obtenir le nombre de tous les commentaires. Cela peut ralentir considérablement le code.

Si votre utilisateur a 1000 commentaires, alors Hibernate fera 1001 requêtes à la base de données pour exécuter ce code, bien qu'il puisse en faire une. Si vous saviez à l'avance que vous auriez besoin de tous les objets de cette classe.