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

好消息是我们可以以不需要从数据库加载额外数据的方式实现我们的方法。坏消息是使用我们的收藏品并不容易。