Apa perbedaan antara JOIN dan JOIN FETCH saat menggunakan JPA dan Hibernate

183

Tolong bantu saya untuk memahami di mana harus menggunakan GABUNG biasa dan di mana GABUNGAN GABUNG.

Sebagai contoh, jika kita memiliki dua pertanyaan ini

FROM Employee emp
JOIN emp.department dep

dan

FROM Employee emp
JOIN FETCH emp.department dep

Apakah ada perbedaan di antara mereka? Jika ya, mana yang harus digunakan kapan?

abbas
sumber
2
Anda dapat menemukannya di sini tautan baca 14.3. Asosiasi dan bergabung
Angga
4
Saya telah melalui dokumentasi itu tetapi masih tidak tahu di mana saya harus menggunakan GABUNG dan di mana GABUNGAN.
abbas
2
Jika Anda memiliki pemetaan @oneToOne diatur ke FetchType.LAZY dan Anda menggunakan kueri kedua (karena Anda perlu objek Departemen untuk dimuat sebagai bagian dari objek Karyawan) yang akan dilakukan Hibernate adalah, itu akan mengeluarkan pertanyaan untuk mengambil objek Departemen untuk setiap objek Karyawan individu itu diambil dari DB. Kemudian dalam kode Anda dapat mengakses objek Departemen melalui asosiasi bernilai tunggal ke Karyawan dan Hibernate tidak akan mengeluarkan pertanyaan apa pun untuk mengambil objek Departemen untuk Karyawan yang diberikan. Ingat Hibernate masih mengeluarkan kueri yang sama dengan jumlah Karyawan yang telah diambilnya.
Bunti
Untuk membantu dalam perburuan doc ~ Strategi Pengambilan
Eddie B
1
@ShameeraAnuranga Saya pikir dalam hal ini Anda akan membutuhkan LEFT OUTER JOIN.
abbas

Jawaban:

180

Dalam dua kueri ini, Anda menggunakan GABUNG untuk meminta semua karyawan yang memiliki setidaknya satu departemen terkait.

Namun, perbedaannya adalah: dalam kueri pertama Anda hanya mengembalikan Pengusaha untuk Hibernate. Di kueri kedua, Anda mengembalikan Karyawan dan semua Departemen terkait.

Jadi, jika Anda menggunakan kueri kedua, Anda tidak perlu melakukan kueri baru untuk membuka basis data lagi untuk melihat Departemen masing-masing Karyawan.

Anda dapat menggunakan permintaan kedua ketika Anda yakin bahwa Anda akan membutuhkan Departemen dari setiap Karyawan. Jika Anda tidak membutuhkan Departemen, gunakan kueri pertama.

Saya sarankan membaca tautan ini jika Anda perlu menerapkan beberapa kondisi WHERE (apa yang mungkin Anda perlukan): Bagaimana cara mengekspresikan JPQL dengan benar "join fetch" dengan "where" klausa sebagai JPA 2 KriteriaQuery?

Memperbarui

Jika Anda tidak menggunakan fetchdan Departemen terus dikembalikan, karena pemetaan Anda antara Karyawan dan Departemen (a @OneToMany) diselesaikan dengan FetchType.EAGER. Dalam hal ini, setiap permintaan HQL (dengan fetchatau tidak) FROM Employeeakan membawa semua Departemen. Ingat bahwa semua pemetaan * ToOne ( @ManyToOnedan @OneToOne) adalah EAGER secara default.

Dherik
sumber
1
Perilaku apa yang akan terjadi jika kita menjalankan pernyataan tanpa mengambil dan mendapatkan hasil. Kemudian dalam sesi kita akan memperlakukan ke departemen?
gstackoverflow
1
@ gstackoverflow, ya
Dherik
Saya menggunakan kueri asli dengan Lazy mengambil di kedua sisi hubungan tetapi masih memuat hierarki hubungan anak.
Badamchi
Layak disebutkan bahwa fetchharus digunakan jika (menggunakan contoh kami) Anda ingin memesan oleh beberapa atribut Departemen. Kalau tidak, (paling tidak berlaku untuk PG) yang mungkin Anda dapatkanERROR: for SELECT DISTINCT, ORDER BY expressions must appear in select list
panjang
60

di tautan ini yang saya sebutkan sebelumnya di komentar, baca bagian ini:

Gabung "ambil" memungkinkan asosiasi atau koleksi nilai diinisialisasi bersama dengan objek induknya menggunakan satu pilihan. Ini sangat berguna dalam hal koleksi. Ini secara efektif mengesampingkan gabungan luar dan deklarasi malas dari file pemetaan untuk asosiasi dan koleksi.

"JOIN FETCH" ini akan berpengaruh jika Anda memiliki (fetch = FetchType.LAZY) properti untuk koleksi di dalam entitas (contoh di bawah).

Dan itu hanya efek metode "ketika permintaan harus terjadi". Dan Anda juga harus tahu ini :

hibernate memiliki dua gagasan ortogonal: kapan asosiasi diambil dan bagaimana diambilnya. Penting agar Anda tidak membingungkan mereka. Kami menggunakan fetch untuk menyesuaikan kinerja. Kita dapat menggunakan lazy untuk mendefinisikan kontrak untuk data apa yang selalu tersedia dalam instance terpisah dari kelas tertentu.

kapan asosiasi diambil -> tipe "FETCH" Anda

bagaimana cara mengambil -> Bergabung / pilih / Subselect / Batch

Dalam kasus Anda, FETCH hanya akan memiliki efek jika Anda memiliki departemen sebagai set di dalam Karyawan, sesuatu seperti ini di entitas:

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

ketika kamu menggunakan

FROM Employee emp
JOIN FETCH emp.department dep

Anda akan mendapatkan empdan emp.dep. ketika Anda tidak menggunakan fetch Anda masih bisa mendapatkan emp.deptetapi hibernate akan memproses pilih lain ke database untuk mendapatkan set departemen itu.

jadi itu hanya masalah penyempurnaan kinerja, tentang Anda ingin mendapatkan semua hasil (Anda membutuhkannya atau tidak) dalam satu permintaan (ingin mengambil), atau Anda ingin meminta yang terakhir ketika Anda membutuhkannya (lazy fetching).

Gunakan pengambilan penuh semangat ketika Anda perlu mendapatkan data kecil dengan satu pilih (satu permintaan besar). Atau gunakan lazy fetching untuk menanyakan apa yang Anda butuhkan belakangan (banyak permintaan lebih kecil).

gunakan ambil ketika:

  • tidak ada koleksi / set besar yang tidak dibutuhkan di dalam entitas yang akan Anda dapatkan

  • komunikasi dari server aplikasi ke server database terlalu jauh dan perlu waktu lama

  • Anda mungkin perlu bahwa koleksi terakhir ketika Anda tidak memiliki akses ke sana ( luar dari transaksional metode / kelas)

Angga
sumber
Bisakah Anda menjelaskannya untuk pertanyaan yang baru saja saya tulis dalam pertanyaan yang diperbarui.
abbas
pertimbangan yang bermanfaat: "tidak ada koleksi / set besar yang tidak dibutuhkan di dalam entitas yang akan Anda dapatkan"
Divs
Apakah departemen masih akan bersemangat diambil jika departemen di dalam karyawan itu Listbukan Set?
Stephane
Apakah menggunakan FETCHkata kunci dalam pernyataan JPQL menyiratkan properti yang diambil dengan penuh semangat?
Stephane
15

IKUTI

Saat menggunakan JOINterhadap asosiasi entitas, JPA akan menghasilkan JOIN antara entitas induk dan tabel entitas anak dalam pernyataan SQL yang dihasilkan.

Jadi, ambil contoh Anda, saat menjalankan permintaan JPQL ini:

FROM Employee emp
JOIN emp.department dep

Hibernate akan menghasilkan pernyataan SQL berikut:

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

Perhatikan bahwa SELECTklausa SQL hanya berisi employeekolom tabel, dan bukan departmentyang. Untuk mengambil departmentkolom tabel, kita harus menggunakan JOIN FETCHalih-alih JOIN.

BERGABUNG FETCH

Jadi, dibandingkan dengan JOIN, JOIN FETCHmemungkinkan Anda untuk memproyeksikan kolom tabel bergabung dalam SELECTklausa pernyataan SQL yang dihasilkan.

Jadi, dalam contoh Anda, saat menjalankan permintaan JPQL ini:

FROM Employee emp
JOIN FETCH emp.department dep

Hibernate akan menghasilkan pernyataan SQL berikut:

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

Perhatikan bahwa, kali ini, departmentkolom tabel juga dipilih, bukan hanya yang terkait dengan entitas yang tercantum dalam FROMklausa JPQL.

Juga, JOIN FETCHmerupakan cara yang bagus untuk mengatasi LazyInitializationExceptionketika menggunakan Hibernate karena Anda dapat menginisialisasi asosiasi entitas menggunakan FetchType.LAZYstrategi pengambilan bersama dengan entitas utama yang Anda ambil.

Vlad Mihalcea
sumber
Apakah mungkin menggunakan beberapa JOIN FETCH dalam kueri yang sama?
A.Onur Özcan
2
Anda dapat BERGABUNG FETCH banyak asosiasi banyak-ke-satu dan satu-ke-satu dan paling banyak satu koleksi. Mengambil banyak koleksi, seperti asosiasi satu-ke-banyak atau banyak-ke-banyak akan berakhir di Produk Cartesian . Namun, jika Anda ingin mengambil beberapa koleksi, Anda dapat menggunakan kueri sekunder untuk koleksi kedua, ketiga, ...,. Lihat artikel ini untuk lebih jelasnya.
Vlad Mihalcea
5

Jika Anda memiliki @oneToOnepemetaan yang diatur ke FetchType.LAZYdan Anda menggunakan kueri kedua (karena Anda perlu objek Departemen untuk dimuat sebagai bagian dari objek Karyawan) yang akan dilakukan Hibernate, itu akan mengeluarkan pertanyaan untuk mengambil objek Departemen untuk setiap objek Karyawan individu yang diambilnya dari DB.

Kemudian, dalam kode Anda dapat mengakses objek Departemen melalui asosiasi tunggal ke Karyawan dan Hibernate tidak akan mengeluarkan kueri apa pun untuk mengambil objek Departemen untuk Karyawan yang diberikan.

Ingat, Hibernate masih mengeluarkan kueri yang sama dengan jumlah Karyawan yang telah diambilnya. Hibernate akan mengeluarkan jumlah kueri yang sama di kedua kueri di atas, jika Anda ingin mengakses objek Departemen semua objek Karyawan

Bunti
sumber
2

Dherik: Saya tidak yakin dengan apa yang Anda katakan, ketika Anda tidak menggunakan fetch hasilnya akan bertipe: List<Object[ ]>yang berarti daftar tabel Object dan bukan daftar Karyawan.

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

Saat Anda menggunakan fetch, hanya ada satu pilih dan hasilnya adalah daftar Karyawan yang List<Employee>berisi daftar keberangkatan. Ini mengesampingkan deklarasi malas entitas.

Bilal BBB
sumber
Saya tidak tahu apakah saya mengerti kekhawatiran Anda. Jika Anda tidak menggunakan fetch, permintaan Anda hanya akan mengembalikan Karyawan. Jika Departemen, bahkan dalam kasus ini, terus dikembalikan, adalah karena pemetaan Anda antara Karyawan dan Departemen (a @OneToMany) diselesaikan dengan FetchType.EAGER. Dalam hal ini, setiap permintaan HQL (dengan fetchatau tidak) FROM Employeeakan membawa semua Departemen.
Dherik
Tanpa menggunakan fetch (hanya istilah gabungan), hasilnya akan menjadi kumpulan koleksi, dua baris, yang pertama adalah koleksi Karyawan dan yang kedua adalah koleksi Departemen. Menggunakan pengambilan cepat atau malas, departemen akan diambil.
Bilal BBB
Tanpa mengambil HQL, ini akan terjadi hanya jika pemetaan Anda antara Karyawan dan Departemen adalah EAGER ( @OneToMany(fetch = FetchType.EAGER). Jika tidak, Departemen tidak akan dikembalikan.
Dherik
@Dichik coba sendiri, Anda akan mendapatkan ClassCastException.
Bilal BBB
Saya mencari tahu masalahnya. Bukan masalah mengambil, tetapi bagaimana selectdibuat di HQL. Coba SELECT emp FROM Employee emp JOIN FETCH emp.department dep. JPA / Hibernate memiliki perilaku ini pengembalian suatu Listdari Object[]ketika Anda ommit SELECTbagian.
Dherik