1.1 Bối cảnh của vấn đề

Khi bắt đầu làm việc với cơ sở dữ liệu thực, bạn sẽ nhớ ngay đến câu “Tối ưu hóa quá sớm là gốc rễ của mọi tội lỗi”. Chỉ bây giờ bạn nhớ cô ấy một cách tiêu cực. Khi làm việc với cơ sở dữ liệu, việc tối ưu hóa là điều không thể thiếu. Và bạn cần phải làm việc với nó ở giai đoạn thiết kế.

Hibernate làm việc với cơ sở dữ liệu rất thuận tiện. Bạn có thể dễ dàng lấy bất kỳ đối tượng con nào chỉ bằng cách chú thích đúng cách @OneToMany@ManyToMany. Ví dụ:


@Entity
@Table(name="user")
class User {
   @Column(name="id")
   public Integer id;
 
   @OneToMany(cascade = CascadeType.ALL)
   @JoinColumn(name = "user_id")
   public List<Comment> comments;
}

Và việc lấy nhận xét của người dùng dễ dàng như thế nào:


User user = session.get(User.class, 1);
List<Comment> comments = user.getComments();

Và bạn sẽ có một bất ngờ lớn. Người dùng có vài nghìn bình luận. Nếu bạn viết mã như thế này, Hibernate tất nhiên sẽ tải tất cả các bình luận của người dùng. Nhưng nó sẽ rất chậm, bình luận sẽ chiếm nhiều bộ nhớ, v.v.

Đó là lý do tại sao bạn không thể viết như vậy! Về lý thuyết thì có, nhưng thực tế thì không.

1.2 Làm mọi thứ trở nên tồi tệ hơn với các bộ sưu tập

Vấn đề thậm chí còn thú vị hơn. Rốt cuộc, thông thường bạn không bao giờ cần tất cả các bình luận của người dùng. Ngay cả khi bạn hiển thị chúng ở đâu đó trên máy khách, bạn vẫn muốn thực hiện theo từng phần - trang.

Vì vậy, bạn cần các phương pháp như thế này:


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

Phương thức đầu tiên chỉ trả về một trang nhận xét - 50 phần. Phương thức thứ hai trả về số trang bình luận. Và đây là điều tồi tệ nhất. Để tìm ra số lượng bình luận một cách đơn giản, bạn phải tải xuống tất cả các bình luận từ cơ sở dữ liệu!

1.3 Ánh sáng cuối đường hầm

Do đó, không ai sử dụng bộ sưu tập trẻ em tuyệt vời của chúng tôi. Không, tất nhiên chúng được sử dụng, nhưng chỉ là một phần của truy vấn HQL. Ví dụ như thế này:


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

Tin vui là chúng ta có thể triển khai các phương thức của mình theo cách mà chúng ta không cần tải thêm dữ liệu từ cơ sở dữ liệu. Tin xấu là không dễ để làm việc với các bộ sưu tập của chúng tôi.