1.1 Contexte du problème

Lorsque vous commencez à travailler avec de vraies bases de données, vous vous souviendrez immédiatement de la phrase "L'optimisation prématurée est la racine de tous les maux". Seulement maintenant vous vous souvenez d'elle d'une manière négative. Lorsque vous travaillez avec une base de données, l'optimisation est indispensable. Et vous devez travailler avec lui dès la phase de conception.

Hibernate rend le travail avec la base de données très pratique. Vous pouvez facilement obtenir n'importe quel objet enfant simplement en annotant correctement @OneToManyet @ManyToMany. Exemple:


@Entity
@Table(name="user")
class User {
   @Column(name="id")
   public Integer id;
 
   @OneToMany(cascade = CascadeType.ALL)
   @JoinColumn(name = "user_id")
   public List<Comment> comments;
}

Et comme il est facile d'obtenir les commentaires de l'utilisateur :


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

Et vous serez dans une grande surprise. L'utilisateur a plusieurs milliers de commentaires. Si vous écrivez un code comme celui-ci, Hibernate chargera bien sûr tous les commentaires de l'utilisateur. Mais ce sera très lent, les commentaires prendront beaucoup de mémoire et ainsi de suite.

C'est pourquoi vous ne pouvez pas écrire comme ça ! En théorie, oui, mais en pratique, non.

1.2 Aggraver les choses avec les collections

Le problème est encore plus intéressant. Après tout, vous n'avez généralement jamais besoin de tous les commentaires de l'utilisateur. Même si vous les affichez quelque part sur le client, vous préférez le faire en parties - pages.

Donc, vous avez besoin de méthodes comme celle-ci:


public class CommentsManager {
    private static final PAGE_SIZE = 50;
 
    public List<Comment> getCommentsPage(int userId, int pageIndex){
     	User user = session.get(User.class, userId);
     	List<Comment> comments = user.getComments();
     	return comments.subList(pageIndex * PAGE_SIZE, PAGE_SIZE);
    }
 
   public int getCommentsPageCount(int userId)   {
     	User user = session.get(User.class, userId);
     	List<Comment> comments = user.getComments();
     	return Math.ceil(  comments.size()/PAGE_SIZE);
   }
 
}

La première méthode renvoie une seule page de commentaires - 50 pièces. La deuxième méthode renvoie le nombre de pages de commentaires. Et c'est le pire. Afin de connaître simplement le nombre de commentaires, il fallait télécharger tous les commentaires de la base de données !

1.3 La lumière au bout du tunnel

Par conséquent, personne n'utilise nos merveilleuses collections pour enfants. Non, bien sûr, ils sont utilisés, mais uniquement dans le cadre de requêtes HQL. Par exemple comme ceci :


public class CommentsManager {
      private static final PAGE_SIZE = 50;
 
       public List<Comment> getCommentsPage(int userId, int pageIndex){
           	String hql = "select comments from User where id = :id";
           	Query<Comment> query = session.createQuery( hql, Comment.class);
           	query.setParametr("id", userId);
           	query.setOffset(pageIndex * PAGE_SIZE);
           	query.setLimit(PAGE_SIZE);
           	return query.list();
      }
 
      public int getCommentsPageCount(int userId)   {
           	String hql = "select count(comments) from User where id = :id";
           	Query<Integer> query = session.createQuery( hql, Integer.class);
           	query.setParametr("id", userId);
           	return Math.ceil(query.singleResult()/PAGE_SIZE);
     }
 
}

La bonne nouvelle est que nous pouvons implémenter nos méthodes de manière à ne pas avoir à charger de données supplémentaires à partir de la base de données. La mauvaise nouvelle, c'est qu'il n'est pas facile de travailler avec nos collections.