1.1 Bakgrund till problemet

När du börjar arbeta med riktiga databaser kommer du omedelbart ihåg frasen "För tidig optimering är roten till allt ont." Först nu minns du henne på ett negativt sätt. När man arbetar med en databas är optimering oumbärlig. Och du måste arbeta med det redan på designstadiet.

Hibernate gör det mycket bekvämt att arbeta med databasen. Du kan enkelt få tag på alla underordnade föremål bara genom att anteckna @OneToManyoch @ManyToMany. Exempel:


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

Och hur lätt det är att få användarens kommentarer:


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

Och du kommer att få en stor överraskning. Användaren har flera tusen kommentarer. Om du skriver kod så här kommer Hibernate givetvis att ladda alla användarens kommentarer. Men det kommer att gå väldigt långsamt, kommentarer kommer att ta upp mycket minne och så vidare.

Det är därför man inte kan skriva så! I teorin, ja, men i praktiken nej.

1.2 Att göra saker värre med samlingar

Problemet är ännu mer intressant. När allt kommer omkring, vanligtvis behöver du aldrig alla användarens kommentarer. Även om du visar dem någonstans på klienten föredrar du att göra det i delar - sidor.

Så du behöver metoder som detta:


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

Den första metoden returnerar endast en sida med kommentarer - 50 stycken. Den andra metoden returnerar antalet sidor med kommentarer. Och det här är det värsta. För att helt enkelt ta reda på antalet kommentarer var du tvungen att ladda ner alla kommentarer från databasen!

1.3 Ljus i slutet av tunneln

Därför är det ingen som använder våra underbara barnkollektioner. Nej, självklart används de, men bara som en del av HQL-frågor. Till exempel så här:


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

Den goda nyheten är att vi kan implementera våra metoder på ett sådant sätt att vi inte behöver ladda extra data från databasen. Den dåliga nyheten är att det inte är lätt att arbeta med våra kollektioner.