Mô tả vấn đề

Như chúng tôi đã nói ở trên, chú thích LazyCollectionOption.EXTRA có vấn đề - nó thực hiện một yêu cầu riêng tới cơ sở dữ liệu cho từng đối tượng. Chúng ta cần giải thích bằng cách nào đó với Hibernate rằng chúng ta muốn nó tải ngay lập tức tất cả các đối tượng con cho các đối tượng cha của chúng ta.

Các nhà phát triển của Hibernate đã đưa ra giải pháp cho vấn đề này, toán tử tìm nạp liên kết trong HQL.

Ví dụ truy vấn HQL:

select distinct task from Task t left join fetch t.employee order by t.deadline

Trong truy vấn này, mọi thứ đều đơn giản và phức tạp cùng một lúc. Hãy cố gắng xây dựng nó từng mảnh một.

lựa chọn 1

Chúng tôi muốn tải xuống tất cả các đối tượngnhiệm vụ, được sắp xếp theo thời hạn. Đây là yêu cầu đó sẽ như thế nào:

select task from Task t order by t.deadline

Trong khi mọi thứ đều rõ ràng. Nhưng lĩnh vực nàyngười lao độngcủa lớp Nhiệm vụ sẽ chứa một tập hợp các Nhân viên được chú thích bằng chú thích EXTRA . Và các đối tượng của bộ sưu tập này sẽ không được tải.

Lựa chọn 2

Buộc Hibernate tải các đối tượng con cho một đối tượngnhiệm vụ.

select task from Task t join fetch t.employee order by t.deadline

Với sự trợ giúp, chúng tôi liên kết rõ ràng các thực thể Nhiệm vụ và Nhân viên trong truy vấn của mình. Hibernate đã biết điều này vì chúng tôi sử dụng chú thích @ManyToMany trên các trường này.

Nhưng chúng ta cần một câu lệnh nối để hoàn thành nó bằng một câu lệnh tìm nạp để nhận một lệnh nối tìm nạp . Đây là cách chúng tôi nói với Hibernate rằng các đối tượng trong bộ sưu tập Task.employee cần được tải từ cơ sở dữ liệu khi yêu cầu của chúng tôi được thực thi.

Tùy chọn 3

Giải pháp trước đó có một vài lỗi. Đầu tiên, sau khi sử dụng phép nối, SQL sẽ không trả lại các đối tượng cho chúng tôinhiệm vụ, không có đối tượng nào được liên kết với chúng trong bảng Nhân viên. Đây chính xác là cách hoạt động của phép nối bên trong .

Vì vậy, chúng ta cần tăng phép nối của mình bằng toán tử left và biến nó thành phép nối trái . Ví dụ:

select task from Task t left join fetch t.employee order by t.deadline

Tùy chọn 4

Nhưng đó không phải là tất cả. Nếu trong mã của bạn, mối quan hệ giữa các thực thể là nhiều-có-thể, thì sẽ có các bản sao trong kết quả truy vấn. cùng một đối tượngnhiệm vụcó thể được tìm thấy trên các nhân viên khác nhau (Đối tượng nhân viên).

Vì vậy, bạn cần thêm từ khóa riêng biệt sau từ chọn để loại bỏ đối tượng Nhiệm vụ trùng lặp.

select distinct task from Task t left join fetch t.employee order by t.deadline

Đây là cách trong 4 bước, chúng tôi đã đạt được yêu cầu mà chúng tôi đã bắt đầu. Chà, mã Java sẽ trông khá được mong đợi:

String hql = " select distinct task from Task t left join fetch t.employee order by t.deadline";
Query<Task> query = session.createQuery( hql, Task.class);
return query.list();

JOIN FETCH Hạn chế

Không ai là hoàn hảo cả. Câu lệnh JOIN FETCH cũng vậy. Nó có khá nhiều hạn chế. Và cách đầu tiên là sử dụng các phương thức setMaxResults()setFirstResult() .

Đối với câu lệnh THAM GIA FETCH, Hibernate của chúng tôi sẽ tạo ra một truy vấn rất phức tạp, trong đó chúng tôi kết hợp ba bảng thành một: nhân viên, nhiệm vụ và employee_task. Trên thực tế, đây không phải là yêu cầu dành cho nhân viên hoặc nhiệm vụ, mà dành cho tất cả các cặp nhân viên-nhiệm vụ đã biết.

Và SQL có thể áp dụng các câu lệnh LIMIT và OFFSET của nó cho chính xác truy vấn đó của các cặp nhân viên-nhiệm vụ. Đồng thời, rõ ràng là từ truy vấn HQL mà chúng tôi muốn nhận chính xác các tác vụ (Tác vụ) và nếu chúng tôi phân phối lại các tham số FirstResult và MaxResult của mình, thì chúng sẽ tham chiếu cụ thể đến các đối tượng Tác vụ.

Nếu bạn viết mã như thế này:

String hql = " select distinct task from Task t left join fetch t.employee order by t.deadline";
Query<Task> query = session.createQuery( hql, Task.class);
       	    query.setFirstResult(0);
        	   query.setMaxResults(1);
return query.list();

Sau đó, Hibernate sẽ không thể chuyển đổi chính xác FirstResult và MaxResult thành các tham số OFFSET và LIMIT của truy vấn SQL.

Thay vào đó, nó sẽ làm ba việc:

  • Truy vấn SQL sẽ chọn tất cả dữ liệu từ bảng và đưa nó trở lại Hibernate
  • Hibernate sẽ chọn các bản ghi cần thiết trong bộ nhớ của nó và trả lại cho bạn
  • Hibernate sẽ đưa ra cảnh báo

Cảnh báo sẽ giống như thế này:

WARN [org.hibernate.hql.internal.ast.QueryTranslatorImpl] HHH000104: 
firstResult/maxResults specified with collection fetch; applying in memory!