1.1 Baggrund for problemet
Når du begynder at arbejde med rigtige databaser, vil du straks huske sætningen "For tidlig optimering er roden til alt ondt." Først nu husker du hende på en negativ måde. Når du arbejder med en database, er optimering uundværlig. Og du skal arbejde med det allerede på designstadiet.
Hibernate gør arbejdet med databasen meget bekvemt. Du kan nemt få ethvert underordnet objekt blot ved at annotere korrekt @OneToMany
og @ManyToMany
. Eksempel:
@Entity
@Table(name="user")
class User {
@Column(name="id")
public Integer id;
@OneToMany(cascade = CascadeType.ALL)
@JoinColumn(name = "user_id")
public List<Comment> comments;
}
Og hvor nemt er det at få brugerens kommentarer:
User user = session.get(User.class, 1);
List<Comment> comments = user.getComments();
Og du får en stor overraskelse. Brugeren har flere tusinde kommentarer. Hvis du skriver kode som denne, vil Hibernate selvfølgelig indlæse alle brugerens kommentarer. Men det vil være meget langsomt, kommentarer vil fylde meget i hukommelsen og så videre.
Derfor kan man ikke skrive sådan! I teorien, ja, men i praksis nej.
1.2 Gør tingene værre med samlinger
Problemet er endnu mere interessant. Når alt kommer til alt, har du normalt aldrig brug for alle brugerens kommentarer. Selvom du viser dem et sted på klienten, foretrækker du at gøre det i dele - sider.
Så du har brug for metoder som denne:
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);
}
}
Den første metode returnerer kun én side med kommentarer - 50 stykker. Den anden metode returnerer antallet af sider med kommentarer. Og det her er det værste. For blot at finde ud af antallet af kommentarer, skulle du downloade alle kommentarerne fra databasen!
1.3 Lys for enden af tunnelen
Derfor er der ingen, der bruger vores skønne børnekollektioner. Nej, selvfølgelig bruges de, men kun som en del af HQL-forespørgsler. For eksempel sådan her:
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);
}
}
Den gode nyhed er, at vi kan implementere vores metoder på en sådan måde, at vi ikke behøver at indlæse ekstra data fra databasen. Den dårlige nyhed er, at det ikke er nemt at arbejde med vores kollektioner.
GO TO FULL VERSION