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);
     }
 
}

好消息是我們可以以不需要從數據庫加載額外數據的方式實現我們的方法。壞消息是使用我們的收藏品並不容易。