Sự khác biệt giữa THAM GIA và THAM GIA FETCH khi sử dụng JPA và Hibernate


183

Vui lòng giúp tôi hiểu nơi sử dụng THAM GIA thường xuyên và nơi THAM GIA THAM GIA.

Ví dụ: nếu chúng ta có hai truy vấn này

FROM Employee emp
JOIN emp.department dep

FROM Employee emp
JOIN FETCH emp.department dep

Có sự khác biệt nào giữa chúng không? Nếu có, sử dụng cái nào khi nào?


2
bạn có thể tìm thấy nó ở đây liên kết đọc 14.3. Các hiệp hội và tham gia
Angga

4
Tôi đã trải qua tài liệu đó nhưng vẫn không biết nên sử dụng THAM GIA ở đâu và THAM GIA THAM GIA ở đâu.
abbas

2
Nếu bạn có ánh xạ @oneToOne được đặt thành FetchType.LAZY và bạn sử dụng truy vấn thứ hai (vì bạn cần tải các đối tượng Bộ phận như một phần của các đối tượng Nhân viên), Hibernate sẽ làm gì, nó sẽ đưa ra các truy vấn để tìm nạp các đối tượng Bộ cho từng đối tượng Nhân viên riêng lẻ nó lấy từ DB. Sau đó, trong mã bạn có thể truy cập các đối tượng của Bộ thông qua Hiệp hội có giá trị đơn lẻ của Bộ và Hibernate sẽ không đưa ra bất kỳ truy vấn nào để tìm nạp đối tượng Bộ cho Nhân viên cụ thể. Hãy nhớ rằng Hibernate vẫn phát hành các truy vấn bằng với số lượng Nhân viên mà nó đã tìm nạp.
Bunti

Để hỗ trợ săn tìm tài liệu ~ Chiến lược tìm nạp
Eddie B

1
@ShameeraAnuranga Tôi nghĩ rằng trong trường hợp đó, bạn sẽ cần THAM GIA TRÁI PHIẾU.
abbas

Câu trả lời:


180

Trong hai truy vấn này, bạn đang sử dụng THAM GIA để truy vấn tất cả các nhân viên có ít nhất một bộ phận liên quan.

Nhưng, sự khác biệt là: trong truy vấn đầu tiên, bạn chỉ trả lại các Việc làm cho Hibernate. Trong truy vấn thứ hai, bạn đang trả lại các Việc làm tất cả các Phòng ban liên quan.

Vì vậy, nếu bạn sử dụng truy vấn thứ hai, bạn sẽ không cần thực hiện truy vấn mới để truy cập lại cơ sở dữ liệu để xem các Phòng ban của mỗi Nhân viên.

Bạn có thể sử dụng truy vấn thứ hai khi bạn chắc chắn rằng bạn sẽ cần Bộ phận của mỗi nhân viên. Nếu bạn không cần Bộ, sử dụng truy vấn đầu tiên.

Tôi khuyên bạn nên đọc liên kết này nếu bạn cần áp dụng một số điều kiện WHERE (những gì bạn có thể sẽ cần): Làm thế nào để diễn đạt chính xác JPQL "tham gia tìm nạp" với mệnh đề "where" như JPA 2 CriteriaQuery?

Cập nhật

Nếu bạn không sử dụng fetchvà các Phòng ban tiếp tục được trả lại, là do bản đồ của bạn giữa Nhân viên và Bộ phận (a @OneToMany) được giải quyết FetchType.EAGER. Trong trường hợp này, mọi fetchtruy vấn HQL (có hoặc không) FROM Employeesẽ mang lại tất cả các Phòng ban. Hãy nhớ rằng tất cả ánh xạ * ToOne ( @ManyToOne@OneToOne) theo mặc định là EAGER.


1
Hành vi nào sẽ xảy ra nếu chúng ta thực thi câu lệnh mà không tìm nạp và nhận kết quả. Sau đó trong phiên chúng tôi sẽ đối xử với bộ phận?
gstackoverflow

1
@gstackoverflow, vâng
Dherik

Tôi sử dụng truy vấn gốc với Lazy fetch trong cả hai mặt của mối quan hệ nhưng vẫn tải thứ bậc của quan hệ con cái.
Badamchi

Đáng nói là fetchphải được sử dụng nếu (sử dụng ví dụ của chúng tôi) bạn muốn đặt hàng theo một số thuộc tính của Bộ. Mặt khác, (ít nhất là hợp lệ cho PG) bạn có thể nhận đượcERROR: for SELECT DISTINCT, ORDER BY expressions must appear in select list
dài

60

trong liên kết này tôi đã đề cập trước khi bình luận, đọc phần này:

Tham gia "tìm nạp" cho phép các liên kết hoặc bộ sưu tập các giá trị được khởi tạo cùng với các đối tượng cha của chúng bằng một lựa chọn duy nhất. Điều này đặc biệt hữu ích trong trường hợp của một bộ sưu tập. Nó có hiệu quả ghi đè các khai báo bên ngoài và lười biếng của tệp ánh xạ cho các hiệp hội và bộ sưu tập.

"THAM GIA FETCH" này sẽ có hiệu lực nếu bạn có thuộc tính (fetch = FetchType.LAZY) cho một bộ sưu tập bên trong thực thể (ví dụ dưới đây).

Và nó chỉ có hiệu lực theo phương pháp "khi truy vấn sẽ xảy ra". Và bạn cũng phải biết điều này :

ngủ đông có hai khái niệm trực giao: khi nào thì liên kết được tìm nạp và nó được tìm nạp như thế nào. Điều quan trọng là bạn không nhầm lẫn chúng. Chúng tôi sử dụng tìm nạp để điều chỉnh hiệu suất. Chúng ta có thể sử dụng lười biếng để xác định hợp đồng cho dữ liệu nào luôn có sẵn trong bất kỳ trường hợp riêng biệt nào của một lớp cụ thể.

khi nào liên kết được tải xuống -> loại "FETCH" của bạn

nó được tải như thế nào -> Tham gia / chọn / Đăng ký / Batch

Trong trường hợp của bạn, FETCH sẽ chỉ có hiệu lực nếu bạn có bộ phận như một bộ bên trong Nhân viên, một cái gì đó như thế này trong thực thể:

@OneToMany(fetch = FetchType.LAZY)
private Set<Department> department;

khi bạn sử dụng

FROM Employee emp
JOIN FETCH emp.department dep

bạn sẽ nhận được empemp.dep. Khi bạn không sử dụng tìm nạp, bạn vẫn có thể nhận được emp.depnhưng hibernate sẽ xử lý một lựa chọn khác vào cơ sở dữ liệu để lấy bộ phận đó.

vì vậy đây chỉ là vấn đề điều chỉnh hiệu suất, về việc bạn muốn nhận được tất cả kết quả (bạn có cần hay không) trong một truy vấn duy nhất (háo hức tìm nạp) hoặc bạn muốn truy vấn nó sau khi bạn cần (tìm nạp lười biếng).

Sử dụng tìm nạp háo hức khi bạn cần lấy dữ liệu nhỏ với một lần chọn (một truy vấn lớn). Hoặc sử dụng tìm nạp lười biếng để truy vấn những gì bạn cần sau này (nhiều truy vấn nhỏ hơn).

sử dụng tìm nạp khi:

  • không có bộ sưu tập lớn không cần thiết / thiết lập bên trong thực thể mà bạn sắp có

  • giao tiếp từ máy chủ ứng dụng đến máy chủ cơ sở dữ liệu quá xa và cần thời gian dài

  • bạn có thể cần rằng bộ sưu tập sau khi bạn không có quyền truy cập vào nó ( ngoài các giao dịch phương pháp / lớp)


Bạn có thể giải thích nó cho các truy vấn mà tôi vừa viết trong câu hỏi cập nhật.
abbas

cân nhắc hữu ích: "không có bộ sưu tập lớn không cần thiết / đặt bên trong thực thể mà bạn sắp có"
Divs

Các phòng ban vẫn sẽ háo hức nếu các bộ phận bên trong nhân viên là một Listthay vì Set?
Stephane

Việc sử dụng FETCHtừ khóa trong một tuyên bố JPQL có nghĩa là một tài sản được lấy một cách háo hức không?
Stephane

15

THAM GIA

Khi sử dụng JOINđể chống lại một hiệp hội thực thể, JPA sẽ tạo THAM GIA giữa thực thể mẹ và các bảng thực thể con trong câu lệnh SQL được tạo.

Vì vậy, lấy ví dụ của bạn, khi thực hiện truy vấn JPQL này:

FROM Employee emp
JOIN emp.department dep

Hibernate sẽ tạo ra câu lệnh SQL sau:

SELECT emp.*
FROM employee emp
JOIN department dep ON emp.department_id = dep.id

Lưu ý rằng SELECTmệnh đề SQL chỉ chứa các employeecột trong bảng chứ không chứa các cột department. Để lấy các departmentcột của bảng, chúng ta cần sử dụng JOIN FETCHthay vì JOIN.

THAM GIA

Vì vậy, so với JOIN, JOIN FETCHcho phép bạn chiếu các cột của bảng tham gia trong SELECTmệnh đề của câu lệnh SQL được tạo.

Vì vậy, trong ví dụ của bạn, khi thực hiện truy vấn JPQL này:

FROM Employee emp
JOIN FETCH emp.department dep

Hibernate sẽ tạo ra câu lệnh SQL sau:

SELECT emp.*, dept.*
FROM employee emp
JOIN department dep ON emp.department_id = dep.id

Lưu ý rằng, lần này, các departmentcột trong bảng cũng được chọn, không chỉ các cột được liên kết với thực thể được liệt kê trong FROMmệnh đề JPQL.

Ngoài ra, JOIN FETCHlà một cách tuyệt vời để giải quyết LazyInitializationExceptionkhi sử dụng Hibernate vì bạn có thể khởi tạo các liên kết thực thể bằng FetchType.LAZYchiến lược tìm nạp cùng với thực thể chính mà bạn đang tìm nạp.


Có thể sử dụng nhiều THAM GIA THAM GIA trong cùng một truy vấn không?
A.Onur Özcan

2
Bạn có thể THAM GIA FETCH nhiều hiệp hội nhiều-một và một-một và nhiều nhất là một bộ sưu tập. Tìm nạp nhiều bộ sưu tập, như liên kết một-nhiều hoặc nhiều-nhiều sẽ kết thúc trong Sản phẩm của Cartesian . Tuy nhiên, nếu bạn muốn tìm nạp nhiều bộ sưu tập, bạn có thể sử dụng các truy vấn thứ cấp cho các bộ sưu tập thứ hai, thứ ba, ..., thứ n. Kiểm tra bài viết này để biết thêm chi tiết.
Vlad Mihalcea

5

Nếu bạn đã @oneToOneđặt ánh xạ FetchType.LAZYvà bạn sử dụng truy vấn thứ hai (vì bạn cần tải các đối tượng Bộ như một phần của đối tượng Nhân viên), Hibernate sẽ làm gì, nó sẽ đưa ra các truy vấn để tìm nạp các đối tượng Bộ cho từng đối tượng Nhân viên mà nó tìm nạp từ DB.

Sau đó, trong mã, bạn có thể truy cập các đối tượng của Bộ thông qua Hiệp hội có giá trị đơn lẻ của Bộ và Hibernate sẽ không đưa ra bất kỳ truy vấn nào để tìm nạp đối tượng Bộ cho Nhân viên cụ thể.

Hãy nhớ rằng, Hibernate vẫn đưa ra các truy vấn bằng với số lượng Nhân viên mà nó đã tìm nạp. Hibernate sẽ đưa ra cùng một số lượng truy vấn trong cả hai truy vấn trên, nếu bạn muốn truy cập các đối tượng Bộ của tất cả các đối tượng Nhân viên


2

Dherik: Tôi không chắc về những gì bạn nói, khi bạn không sử dụng tìm nạp, kết quả sẽ thuộc loại: List<Object[ ]>có nghĩa là danh sách các bảng Object chứ không phải danh sách Nhân viên.

Object[0] refers an Employee entity 
Object[1] refers a Departement entity 

Khi bạn sử dụng tìm nạp, chỉ có một lựa chọn và kết quả là danh sách Nhân viên List<Employee>có chứa danh sách khởi hành. Nó ghi đè lên tuyên bố lười biếng của thực thể.


Tôi không biết nếu tôi hiểu mối quan tâm của bạn. Nếu bạn không sử dụng fetch, truy vấn của bạn sẽ chỉ trả về Nhân viên. Nếu các Phòng ban, ngay cả trong trường hợp này, tiếp tục được trả lại, là do ánh xạ giữa Nhân viên và Bộ phận (một @OneToMany) được giải quyết với FetchType.EAGER. Trong trường hợp này, mọi fetchtruy vấn HQL (có hoặc không) FROM Employeesẽ mang lại tất cả các Phòng ban.
Dherik

Không sử dụng tìm nạp (chỉ tham gia thuật ngữ), kết quả sẽ là một mảng các bộ sưu tập, hai hàng, đầu tiên là một bộ sưu tập Nhân viên và thứ hai là một bộ sưu tập của các Phòng ban. Sử dụng tìm nạp háo hức hoặc tìm nạp lười biếng, các bộ phận sẽ được tìm nạp.
BBB song phương

Nếu không tìm nạp HQL, điều này sẽ chỉ xảy ra nếu ánh xạ giữa Nhân viên và Bộ phận của bạn là EAGER ( @OneToMany(fetch = FetchType.EAGER). Nếu không phải là trường hợp, các Sở sẽ không được trả lại.
Dherik

@Dherik hãy tự mình thử, Bạn sẽ nhận được ClassCastException.
BBB song phương

Tôi tìm ra vấn đề. Không phải là một vấn đề tìm nạp, nhưng làm thế nào selectđược thực hiện trong HQL. Hãy thử SELECT emp FROM Employee emp JOIN FETCH emp.department dep. JPA / Hibernate có hành vi này trở lại một Listtrong Object[]khi bạn ommit SELECTphần.
Dherik
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.