1.1 Background of the problem

When you start working with real databases, you will immediately remember the phrase “Premature optimization is the root of all evil.” Only now you remember her in a negative way. When working with a database, optimization is indispensable. And you need to work with it already at the design stage.

Hibernate makes working with the database very convenient. You can easily get any child objects just by properly annotating @OneToManyand @ManyToMany. Example:


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

And how easy it is to get the user's comments:


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

And you will be in for a big surprise. The user has several thousand comments. If you write code like this, Hibernate will of course load all of the user's comments. But it will be very slow, comments will take up a lot of memory and so on.

That's why you can't write like that! In theory, yes, but in practice, no.

1.2 Making things worse with collections

The problem is even more interesting. After all, usually you never need all the user's comments. Even if you display them somewhere on the client, you prefer to do it in parts - pages.

So you need methods like this:


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

The first method returns only one page of comments - 50 pieces. The second method returns the number of pages of comments. And this is the worst. In order to simply find out the number of comments, you had to download all the comments from the database!

1.3 Light at the end of the tunnel

Therefore, no one uses our wonderful child collections. No, of course they are used, but only as part of HQL queries. For example like this:


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

The good news is that we can implement our methods in such a way that we do not need to load extra data from the database. The bad news is that it's not easy to work with our collections.