1.1 Предыстория проблемы
Когда ты начнешь работать с реальными базами данных, то тут же вспомнишь фразу “Преждевременная оптимизация – корень всех зол”. Только вот вспомнишь ты ее в негативном ключе. При работе с базой данных без оптимизации не обойтись. И работать с ней нужно уже на этапе проектирования.
Hibernate делает работу с базой данных очень удобной. Ты легко можешь получить любые дочерние объекты, просто правильно расставив аннотации @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();
И тебя тут будет ждать большой сюрприз. У пользователя несколько тысяч комментариев. Если ты напишешь такой код, то 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);
}
}
Первый метод возвращает только одну страницу комментариев – 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);
}
}
Хорошая новость – мы можем реализовать наши методы так, чтобы не нужно было загружать лишние данные из базы. Плохая новость – так просто работать с нашими коллекциями нельзя.
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ