1.1 Contextul problemei
Când începeți să lucrați cu baze de date reale, vă veți aminti imediat expresia „Optimizarea prematură este rădăcina tuturor relelor”. Abia acum îți amintești de ea într-un mod negativ. Când lucrați cu o bază de date, optimizarea este indispensabilă. Și trebuie să lucrați cu el deja în faza de proiectare.
Hibernate face lucrul cu baza de date foarte convenabil. Puteți obține cu ușurință orice obiecte copil doar adnotând @OneToMany
și @ManyToMany
. Exemplu:
@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 cât de ușor este să obții comentariile utilizatorului:
User user = session.get(User.class, 1);
List<Comment> comments = user.getComments();
Și vei avea o mare surpriză. Utilizatorul are câteva mii de comentarii. Dacă scrieți astfel de cod, Hibernate va încărca, desigur, toate comentariile utilizatorului. Dar va fi foarte lent, comentariile vor ocupa multă memorie și așa mai departe.
De aceea nu poți scrie așa! În teorie, da, dar în practică, nu.
1.2 Înrăutățirea lucrurilor cu colecțiile
Problema este și mai interesantă. La urma urmei, de obicei nu aveți nevoie niciodată de toate comentariile utilizatorului. Chiar dacă le afișați undeva pe client, preferați să o faceți în părți - pagini.
Deci, aveți nevoie de metode ca aceasta:
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);
}
}
Prima metodă returnează doar o pagină de comentarii - 50 de bucăți. A doua metodă returnează numărul de pagini de comentarii. Și acesta este cel mai rău. Pentru a afla pur și simplu numărul de comentarii, a trebuit să descărcați toate comentariile din baza de date!
1.3 Lumină la capătul tunelului
Prin urmare, nimeni nu folosește minunatele noastre colecții pentru copii. Nu, desigur că sunt folosite, dar numai ca parte a interogărilor HQL. De exemplu astfel:
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);
}
}
Vestea bună este că putem implementa metodele noastre în așa fel încât să nu fie nevoie să încărcăm date suplimentare din baza de date. Vestea proastă este că nu este ușor să lucrezi cu colecțiile noastre.