1.1 Предистория на проблема

Когато започнете да работите с реални бази данни, веднага ще си спомните фразата „Преждевременната оптимизация е коренът на всяко зло“. Само сега си спомняш за нея по негативен начин. При работа с база данни оптимизацията е незаменима. И трябва да работите с него още на етапа на проектиране.

Hibernate прави работата с базата данни много удобна. Можете лесно да получите всяHowви дъщерни обекти само чрез правилно анотиране @OneToManyи @ManyToMany. Пример:


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

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


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

И ще ви очаква голяма изненада. Потребителят има няколко хиляди коментара. Ако напишете code по този начин, Hibernate разбира се ще зареди всички коментари на потребителя. Но ще е много бавно, коментарите ще заемат много памет и т.н.

Затова не може да се пише така! На теория да, но на практика не.

1.2 Влошаване на нещата с колекциите

Проблемът е още по-интересен. В крайна сметка обикновено никога не се нуждаете от всички коментари на потребителя. Дори и да ги показваш някъде на клиента, предпочиташ да го правиш на части - страници.

Така че имате нужда от методи като този:


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

Първият метод връща само една page с коментари - 50 броя. Вторият метод връща броя на страниците с коментари. И това е най-лошото. За да разберете просто броя на коментарите, трябваше да изтеглите всички коментари от базата данни!

1.3 Светлина в края на тунела

Следователно никой не използва нашите прекрасни детски колекции. Не, разбира се, че се използват, но само като част от HQL заявки. Например така:


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

Добрата новина е, че можем да приложим нашите методи по такъв начин, че да не се налага да зареждаме допълнителни данни от базата данни. Лошата новина е, че не е лесно да се работи с нашите колекции.