1.1 Tło problemu
Kiedy zaczniesz pracować z prawdziwymi bazami danych, od razu przypomnisz sobie zdanie „Przedwczesna optymalizacja jest źródłem wszelkiego zła”. Dopiero teraz wspominasz ją w negatywny sposób. Podczas pracy z bazą danych optymalizacja jest niezbędna. I musisz z nim pracować już na etapie projektowania.
Hibernate sprawia, że praca z bazą danych jest bardzo wygodna. Możesz łatwo uzyskać dowolne obiekty podrzędne, po prostu odpowiednio dodając adnotacje @OneToMany
i pliki @ManyToMany
. Przykład:
@Entity @Table(name="user")
class User {
@Column(name="id")
public Integer id;
@OneToMany(cascade = CascadeType.ALL) @JoinColumn(name = "user_id")
public List<Comment> comments;
}
I jak łatwo jest uzyskać komentarze użytkownika:
User user = session.get(User.class, 1);
List<Comment> comments = user.getComments();
I czeka cię wielka niespodzianka. Użytkownik ma kilka tysięcy komentarzy. Jeśli napiszesz taki kod, Hibernate oczywiście załaduje wszystkie komentarze użytkownika. Ale będzie to bardzo powolne, komentarze zajmą dużo pamięci i tak dalej.
Dlatego nie możesz tak pisać! W teorii tak, ale w praktyce nie.
1.2 Pogorszenie sytuacji dzięki kolekcjom
Problem jest jeszcze ciekawszy. W końcu zwykle nigdy nie potrzebujesz wszystkich komentarzy użytkownika. Nawet jeśli wyświetlasz je gdzieś na kliencie, wolisz robić to w częściach - stronach.
Potrzebujesz więc takich metod:
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);
}
}
Pierwsza metoda zwraca tylko jedną stronę komentarzy - 50 sztuk. Druga metoda zwraca liczbę stron komentarzy. I to jest najgorsze. Aby po prostu sprawdzić liczbę komentarzy, trzeba było pobrać wszystkie komentarze z bazy danych!
1.3 Światełko na końcu tunelu
Dlatego nikt nie korzysta z naszych wspaniałych kolekcji dziecięcych. Nie, oczywiście są używane, ale tylko w ramach zapytań HQL. Na przykład tak:
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);
}
}
Dobra wiadomość jest taka, że możemy zaimplementować nasze metody w taki sposób, że nie musimy ładować dodatkowych danych z bazy danych. Zła wiadomość jest taka, że nie jest łatwo pracować z naszymi kolekcjami.
GO TO FULL VERSION