JPA ve Hibernate kullanırken JOIN ve JOIN FETCH arasındaki fark nedir


183

Lütfen normal bir JOIN'in nerede ve bir JOIN FETCH'in nerede kullanılacağını anlamama yardımcı olun.

Örneğin, bu iki sorgumuz varsa

FROM Employee emp
JOIN emp.department dep

ve

FROM Employee emp
JOIN FETCH emp.department dep

Aralarında fark var mı? Evet ise, hangisi ne zaman kullanılır?


2
buradan bulabilirsiniz bağlantısını okumak 14.3. Dernekler ve Birleşimler
Angga

4
Bu dokümanı inceledim ama nereden bir JOIN ve nereye bir JOIN FETCH kullanmalıyım bilmiyorum.
abbas

2
@ OneToOne eşlemesini FetchType.LAZY olarak ayarladıysanız ve Hazırda Bekleme'nin yapacağı ikinci sorgu kullanıyorsanız (Departman nesnelerinin Çalışan nesnelerinin bir parçası olarak yüklenmesi gerektiğinden), her bir Employee nesnesi için Departman nesnelerini almak için sorgular yayınlar DB'den alır. Kodun daha sonra Departman nesnelerine Çalışandan Departmana tek değerli ilişkilendirme yoluyla erişebilirsiniz ve Hazırda Beklet, belirtilen Çalışan için Departman nesnesini getirmek için herhangi bir sorgu göndermez. Hazırda Beklet'in hala getirdiği Çalışan sayısına eşit sorgular yayınladığını unutmayın.
Bunti

Doktora avına yardımcı olmak için ~ Getirme Stratejileri
Eddie B

1
@ShameeraAnuranga Bu durumda bir SOL DIŞ KATILMAYA ihtiyacınız olacağını düşünüyorum.
abbas

Yanıtlar:


181

Bu iki sorguda, en az bir bölüm ilişkilendirilmiş tüm çalışanları sorgulamak için JOIN kullanıyorsunuz.

Ancak, fark şu: ilk sorguda yalnızca Hazırda Bekletme için Çalışanlar döndürüyorsunuz. İkinci sorguda, employés iade ve ilişkili tüm Bölümleri.

Bu nedenle, ikinci sorguyu kullanırsanız, her bir Çalışanın Departmanlarını görmek için veritabanına tekrar vurmak için yeni bir sorgu yapmanız gerekmez.

Her bir Çalışanın Departmanına ihtiyacınız olduğundan emin olduğunuzda ikinci sorguyu kullanabilirsiniz. Departmana ihtiyacınız yoksa, ilk sorguyu kullanın.

Bazı WHERE koşulu (muhtemelen ihtiyacınız olacak) uygulamak gerekiyorsa bu bağlantıyı okumanızı tavsiye ederim: Nasıl düzgün bir şekilde JPQL "join fetch" ile JPA 2 CriteriaQuery olarak "nerede" yan tümcesini ifade etmek?

Güncelleme

Eğer kullanmıyorsanız fetchve Bölümler döndürülmesine devam Çalışan ve Bölümünde (a arasında haritalama, çünkü olan @OneToMany) ile setted edilir FetchType.EAGER. Bu durumda, herhangi bir HQL (ile fetchveya değil) sorgusu FROM Employeetüm Bölümleri getirecektir. * ToOne ( @ManyToOneve @OneToOne) eşlemelerinin varsayılan olarak EAGER olduğunu unutmayın.


1
İfadeyi getirmeden ve sonuç almadan yürütürsek hangi davranış olur? O zaman oturumda departmana tedavi mi edeceğiz?
gstackoverflow

1
@gstackoverflow, evet
Dherik

Ben ilişkinin her iki tarafında Lazy getirme ile yerel sorgu kullanın ama yine de çocuk ilişkileri hiyerarşisini yükler.
Badamchi

fetchBazı departman özniteliklerine göre sipariş vermek istiyorsanız (örneğimizi kullanarak) kullanılması gerektiğini belirtmek gerekir. Aksi takdirde, (en azından PG için geçerli) alabilirsinizERROR: for SELECT DISTINCT, ORDER BY expressions must appear in select list
uzun

60

içinde bu bağlantı ı yorumum önce belirtildiği, bu bölümü okuyun:

Bir "getirme" birleşimi, ilişkilendirmelerin veya değer koleksiyonlarının, tek bir seçim kullanılarak üst nesneleriyle birlikte başlatılmasına olanak tanır. Bu özellikle bir koleksiyon söz konusu olduğunda kullanışlıdır. İlişkilendirmeler ve koleksiyonlar için eşleme dosyasının dış birleştirme ve tembel bildirimlerini etkili bir şekilde geçersiz kılar .

varlık içindeki bir koleksiyon için (fetch = FetchType.LAZY) özelliğiniz varsa bu "FETCH JOIN" etkisi olacaktır (örnek feryat).

Ve sadece "sorgunun ne zaman gerçekleşmesi gerektiği" yöntemini etkiler. Ve ayrıca bilmelidir bu :

hazırda bekletme modunun iki dikey görüşü vardır: ilişkilendirme ne zaman getirilir ve nasıl getirilir. Onları karıştırmamanız önemlidir. Performansı ayarlamak için getirmeyi kullanırız. Belirli bir sınıfın herhangi bir müstakil örneğinde hangi verilerin her zaman kullanılabilir olduğuna ilişkin bir sözleşme tanımlamak için tembel kullanabiliriz.

ilişkilendirme ne zaman getirilir -> "FETCH" türünüz

nasıl getirilir -> Katıl / Seç / Alt Seç / Toplu

Sizin durumunuzda, FETCH sadece Çalışanın içinde bir set olarak bölümünüz varsa, bu varlıkta böyle bir şey varsa etkili olacaktır:

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

kullandığında

FROM Employee emp
JOIN FETCH emp.department dep

alacaksın empve emp.dep. Getirmeyi kullanmadığınız zaman hala alabilirsiniz emp.depama hazırda bekletme bölüm kümesi almak için veritabanına başka bir seçim işleyecek.

bu yüzden sadece bir performans ayarlama meselesi, tek bir sorguda (istekli olsun ya da olmasın) tüm sonucu almak istediğiniz (istekli getirme) ya da ihtiyacınız olduğunda ikinci olarak sorgulamak istediğinizde (tembel getirme).

Tek bir seçimle (bir büyük sorgu) küçük veri almanız gerektiğinde istekli getirmeyi kullanın. Ya da ihtiyacınız olanı sorgulamak için tembel getirmeyi kullanın (çok daha küçük sorgu).

şu durumlarda getir:

  • almak istediğiniz o varlığın içinde büyük gereksiz koleksiyon / set yok

  • uygulama sunucusundan veritabanı sunucusuna çok uzak iletişim ve uzun zamana ihtiyacı var

  • Eğer (bu bilgilere erişimi olmadığı zamanlarda İkincisini bu koleksiyonu gerekebilir dışında bir işlem yöntemi / sınıfa)


Güncellenmiş soruya yeni yazdığım sorular için açıklar mısınız?
abbas

yararlı bir düşünce: "almak istediğiniz o varlık içinde büyük gereksiz koleksiyon / set yok"
Divs

Çalışanın içindeki departmanlar bir Listyerine bir ise, departmanlar hala hevesle getirilir Setmi?
Stephane

FETCHAnahtar kelimeyi JPQL deyiminde kullanmak, hevesle alınan bir özelliği mi ima eder?
Stephane

15

KATILMAK

Bir JOINvarlık ilişkilendirmelerine karşı kullanıldığında , JPA, oluşturulan SQL deyiminde üst varlık ve alt varlık tabloları arasında bir JOIN oluşturur.

Bu JPQL sorgusunu yürütürken örneğinizi alarak:

FROM Employee emp
JOIN emp.department dep

Hazırda Bekletme aşağıdaki SQL deyimini oluşturacaktır:

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

SQL SELECTdeyiminin yalnızca employeetablo sütunlarını içerdiğini, yalnızca tablo sütunlarını içerdiğini unutmayın department. departmentTablo sütunlarını getirmek için JOIN FETCHyerine kullanmamız gerekir JOIN.

FETCH'E KATIL

Yani, karşılaştırıldığında , birleştirilmiş tablo sütunları oluşturulan SQL deyimi yan tümcesinde yansıtmak JOINiçin JOIN FETCHizin verir SELECT.

Örneğin, bu JPQL sorgusunu yürütürken:

FROM Employee emp
JOIN FETCH emp.department dep

Hazırda Bekletme aşağıdaki SQL deyimini oluşturacaktır:

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

Bu kez, departmentyalnızca FROMJPQL yan tümcesinde listelenen varlıkla ilişkili olanlar değil , tablo sütunlarının da seçildiğini unutmayın .

Ayrıca, getirme stratejisini kullanarak varlık ilişkilerini başlatabileceğiniz için Hazırda Beklet'i kullanırken ne zaman JOIN FETCHele LazyInitializationExceptionalacağınızı ele almanın harika bir yoludur FetchType.LAZY.


Aynı sorguda birden fazla JOIN FETCH kullanmak mümkün müdür?
A.Onur Özcan

2
FETCH'e birden çok bire bir ve bire bir dernek ve en fazla bir koleksiyona KATILABİLİRSİNİZ. Bir-çok veya çok-çok dernekler gibi birden fazla koleksiyon getirmek, bir Kartezyen Ürünle sonuçlanacaktır . Ancak, birden çok koleksiyon almak istiyorsanız, ikinci, üçüncü, ..., n. Koleksiyonlar için ikincil sorgular kullanabilirsiniz. Daha fazla ayrıntı için bu makaleye göz atın .
Vlad Mihalcea

5

Eğer varsa @oneToOneeşleme seti FetchType.LAZYve ne Hazırda yapacak (eğer Bölümü Çalışan parçası nesneler olarak yüklenecek nesneleri gerekir çünkü) ikinci sorguyu kullanmak, bu Bölümü onu DB'den getirir her bireyin Çalışan nesne için nesneleri getirmek için sorgular yayınlayacaktır.

Daha sonra kodda, Departman nesnelerine Çalışandan Departmana tek değerli ilişkilendirme yoluyla erişebilirsiniz ve Hazırda Beklet, belirtilen Çalışana Departman nesnesini getirmek için herhangi bir sorgu göndermez.

Hazırda Bekletme modunun hâlâ getirdiği Çalışan sayısına eşit sorgular yayınladığını unutmayın. Tüm Çalışan nesnelerinin Departman nesnelerine erişmek istiyorsanız, Hazırda Beklet, yukarıdaki sorguların her ikisinde de aynı sayıda sorgu yayınlar


2

Dherik: Ne dediğinden emin değilim, getirme işlevini kullanmadığınızda sonuç tür olacaktır: List<Object[ ]>bu, Çalışanların listesi değil, Nesne tablolarının bir listesi anlamına gelir.

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

Getirmeyi kullandığınızda, yalnızca bir seçim olur ve sonuç List<Employee>bölüm listesini içeren Çalışan listesi olur. Varlığın tembel beyanını geçersiz kılar.


Endişenizi anlayıp anlamadığımı bilmiyorum. Kullanmazsanız fetch, sorgunuz yalnızca Çalışanları döndürür. Departmanlar, bu durumda bile iade edilmeye devam ederse, Çalışan ve Departman arasındaki eşlemenizin (bir @OneToMany) FetchType.EAGER ile ayarlanmış olmasından kaynaklanmaktadır. Bu durumda, herhangi bir HQL (ile fetchveya değil) sorgusu FROM Employeetüm Bölümleri getirecektir.
Dherik

Getirmeyi kullanmadan (yalnızca katılma terimi), sonuç bir koleksiyon dizisi, iki sıra olacak, birincisi Çalışanların bir koleksiyonudur ve ikincisi Departmanların bir koleksiyonudur. İstekli getirme veya tembel getirme kullanıldığında departmanlar getirilir.
Bilal BBB

HQL getirilmezse, bu sadece Çalışan ile Departman arasındaki eşlemeniz EAGER ( @OneToMany(fetch = FetchType.EAGER) ise gerçekleşir. Durum böyle değilse, Departmanlar iade edilmeyecektir.
Dherik

@Dherik kendiniz deneyin, bir ClassCastException alırsınız.
Bilal BBB

Sorunu çözdüm. Getirme sorunu değil, ama selectHQL nasıl yapıldı. Deneyin SELECT emp FROM Employee emp JOIN FETCH emp.department dep. JPA / hazırda dönüş a bu davranışa sahip Listbir Object[]sen ommit zaman SELECTparçasını.
Dherik
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.