1.1 Bakgrunn for problemet
Når du begynner å jobbe med ekte databaser, vil du umiddelbart huske setningen "For tidlig optimalisering er roten til alt ondt." Først nå husker du henne på en negativ måte. Når du arbeider med en database, er optimalisering uunnværlig. Og du må jobbe med det allerede på designstadiet.
Hibernate gjør arbeidet med databasen veldig praktisk. Du kan enkelt få tak i alle underordnede objekter bare ved å kommentere @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 enkelt det er å få brukerens kommentarer:
User user = session.get(User.class, 1);
List<Comment> comments = user.getComments();
Og du vil få en stor overraskelse. Brukeren har flere tusen kommentarer. Hvis du skriver kode som dette, vil Hibernate selvfølgelig laste alle brukerens kommentarer. Men det vil gå veldig sakte, kommentarer vil ta opp mye minne og så videre.
Derfor kan du ikke skrive sånn! I teorien, ja, men i praksis nei.
1.2 Gjør ting verre med samlinger
Problemet er enda mer interessant. Tross alt, vanligvis trenger du aldri alle brukerens kommentarer. Selv om du viser dem et sted på klienten, foretrekker du å gjøre det i deler - sider.
Så du trenger metoder som dette:
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 metoden returnerer bare én side med kommentarer - 50 stykker. Den andre metoden returnerer antall sider med kommentarer. Og dette er det verste. For ganske enkelt å finne ut antall kommentarer, måtte du laste ned alle kommentarene fra databasen!
1.3 Lys i enden av tunnelen
Derfor er det ingen som bruker våre fantastiske barnesamlinger. Nei, selvfølgelig brukes de, men bare som en del av HQL-spørringer. For eksempel slik:
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 nyheten er at vi kan implementere metodene våre på en slik måte at vi ikke trenger å laste inn ekstra data fra databasen. Den dårlige nyheten er at det ikke er lett å jobbe med samlingene våre.