1.1 Hintergrund des Problems

Wenn Sie anfangen, mit echten Datenbanken zu arbeiten, werden Sie sich sofort an den Satz „Vorzeitige Optimierung ist die Wurzel allen Übels“ erinnern. Erst jetzt erinnerst du dich negativ an sie. Bei der Arbeit mit einer Datenbank ist eine Optimierung unabdingbar. Und Sie müssen bereits in der Entwurfsphase damit arbeiten.

Hibernate macht die Arbeit mit der Datenbank sehr komfortabel. Sie können problemlos alle untergeordneten Objekte erhalten, indem Sie einfach und richtig mit Anmerkungen @OneToManyversehen @ManyToMany. Beispiel:


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

Und wie einfach es ist, die Kommentare des Benutzers zu erhalten:


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

Und Sie werden eine große Überraschung erleben. Der Benutzer hat mehrere tausend Kommentare. Wenn Sie solchen Code schreiben, lädt Hibernate natürlich alle Kommentare des Benutzers. Aber es wird sehr langsam sein, Kommentare werden viel Speicher beanspruchen und so weiter.

Deshalb kann man so nicht schreiben! Theoretisch ja, aber in der Praxis nein.

1.2 Die Sache mit Sammlungen noch schlimmer machen

Das Problem ist noch interessanter. Schließlich braucht man in der Regel nie alle Kommentare des Benutzers. Selbst wenn Sie sie irgendwo auf dem Client anzeigen, möchten Sie dies lieber in Teilen tun – in Seiten.

Sie benötigen also Methoden wie diese:


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

Die erste Methode gibt nur eine Seite mit Kommentaren zurück – 50 Stück. Die zweite Methode gibt die Anzahl der Kommentarseiten zurück. Und das ist das Schlimmste. Um einfach die Anzahl der Kommentare herauszufinden, musste man alle Kommentare aus der Datenbank herunterladen!

1.3 Licht am Ende des Tunnels

Daher nutzt niemand unsere wunderbaren Kinderkollektionen. Nein, natürlich werden sie verwendet, aber nur im Rahmen von HQL-Abfragen. Zum Beispiel so:


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

Die gute Nachricht ist, dass wir unsere Methoden so implementieren können, dass wir keine zusätzlichen Daten aus der Datenbank laden müssen. Die schlechte Nachricht ist, dass es nicht einfach ist, mit unseren Sammlungen zu arbeiten.