join fetch ve Sayfalama — 10 Sipariş İçin Bütün Tablo
Önce şunu oku: Flush ve SQL Sırası — save() Satırında Neden Hiçbir Şey Olmuyor?
30 saniyede özet
Koleksiyonu join fetch ile çekip aynı sorguyu sayfalarsan Hibernate LIMIT'i SQL'den çıkarır ve sayfayı bellekte keser. Doğrusu iki küçük sorgu: önce sayfanın id'leri, sonra yalnızca onların detayı.
Ekranda yalnızca on sipariş var. Testte her şey hızlıydı, canlıda sayfa saniyelerce açılmıyor. Bellek grafiği her tıklamada sıçrıyor.
-
Bayt: Siparişleri satırlarıyla birlikte, sayfa sayfa gösteriyorum. Tek sorgu, çok şık.
-
Sen: Testte kaç sipariş vardı?
-
Bayt: Elli. Canlıda on bin... ama ben yine on tane istiyorum!
-
Bayt: Sen on tane istiyorsun. Veritabanı kaç tane gönderiyor, ona bakalım.
Sayfayı veritabanı keser
LIMIT 10 OFFSET 20 veritabanına “yirmi satırı atla, on tane ver” der. Kesme işini veritabanı yapar ve ağdan yalnızca on satır geçer.
Ama siparişi satırlarıyla birlikte çekmek için tabloları birleştirirsin. Bu birleştirmede bir sipariş, satır sayısı kadar sonuç satırı üretir. On sonuç satırı artık on sipariş demek değildir.
Kafam karıştı, daha basit anlat
Veritabanı on satır keser, sipariş saymaz. Üç satırlık bir sipariş üç sonuç satırı tutar.
LIMIT 10 OFFSET 20 veritabanına ne söyler?
Bir sipariş 3 satır (line) içeriyor. orders JOIN order_lines sorgusunda bu sipariş kaç sonuç satırı üretir?
join fetch sayfalamayla buluşunca
select o from Order o join fetch o.lines sorgusuna setFirstResult(20) ve setMaxResults(10) verdin. Tabloda 10.000 sipariş var. Hibernate SQL'e ne yazar? Cevabı göster
Hiç LIMIT yazmaz. Sonuç satırlarını sipariş sınırında kesemeyeceği için bütün siparişleri satırlarıyla getirir, sonra on tanesini bellekte ayırır. Log’a yalnızca bir uyarı düşer.
Adım adım oku
- Sayfa 3 istendi: on sipariş ve satırları.
- LIMIT SQL'den çıkarıldı; bütün siparişler bütün satırlarıyla geldi.
- On tanesi bellekte ayrıldı, gerisi atıldı.
- Doğru yol: önce on id'yi LIMIT ile al, sonra yalnızca onları getir.
fetch joinJPQL'de join fetch: ilişkili entity ya da koleksiyonu aynı sorguda doldurur. Koleksiyonla birlikte sayfalanırsa LIMIT düşer ve sayfa bellekte kesilir.Sözlükte gör → bir koleksiyonu tek sorguda doldurur. Sayfalamayla birleşince Hibernate HHH90003004 uyarısını yazar: “applying in memory”. Hata yok, sadece yavaş ve aç gözlü bir sorgu.
hibernate.query.fail_on_pagination_over_collection_fetch=true ayarı bu uyarıyı hataya çevirir. Sorun böylece ilk testte görünür.
Kafam karıştı, daha basit anlat
join fetch ve sayfalama aynı sorguda buluşursa sayfa bellekte kesilir. Ayarı açarsan Hibernate bunu yapmak yerine hata verir.
join fetch o.lines olan bir sorguya setMaxResults(10) verildi. Hibernate ne yapar?
hibernate.query.fail_on_pagination_over_collection_fetch=true ne işe yarar?
İki küçük sorgu
Birinci sorgu yalnızca sayfadaki siparişlerin id’lerini LIMIT ile alır. İkinci sorgu o id’ler için join fetch yapar. Kesme veritabanında kalır ve yalnızca gereken satırlar gelir.
Diğer yol batch fetchingTembel koleksiyon ya da ilişkileri tek tek değil, birkaçını bir IN sorgusuyla yükleme. default_batch_fetch_size ya da @BatchSize ile açılır; N+1'i küçültür.Sözlükte gör →: sayfayı tembel yüklersin, satırlara ilk dokunuşta Hibernate on siparişin satırlarını tek bir IN sorgusuyla getirir. Bu, N+1Bir sorgu listeyi getirir, sonra her eleman için bir sorgu daha atılır. Her biri hızlı olduğu için yavaş sorgu logunda görünmez; yavaşlayan şey toplam gidiş-dönüş sayısıdır.Sözlükte gör → sorununu da iki sorguya indirir.
Kafam karıştı, daha basit anlat
Önce on numarayı al, sonra yalnızca o on siparişi satırlarıyla getir. İki kısa yolculuk, bir dev yolculuktan iyidir.
Siparişleri satırlarıyla sayfalamanın doğru iki sorgulu yolu hangisi?
Kendin gör
Sayfa 3, satırlarıyla birlikte
Tohum 272354Oynat ya da adımla.
Şu an ne oldu?
join fetch + sayfalama
Hedef hep aynı: 3. sayfadaki 10 sipariş ve her birinin satırları.
Görevler0/3
10 sipariş için 10.000 siparişi belleğe yükleaçık
İpucu
Tablo büyüklüğünü değiştir.
Aynı sayfayı 11 sorguyla getiraçık
İpucu
Hiçbir şeyi önceden yükleme.
10.000 siparişlik tabloda sayfayı 2 sorguyla, bellekte kesmeden getiraçık
İpucu
İki yol var.
Olay günlüğü (0)
Henüz olay yok. Oynat veya adımla.
- Varsayılanla oynat. LIMIT yok, 50 siparişin hepsi belleğe geldi.
- Tabloyu 10.000 yap. Aynı on sipariş için on bin sipariş kuruldu.
- “Bellekte sayfalamayı yasakla”yı aç. Sorgu çalışmadan hata verdi.
- Tembel yüklemeyi seç. Doğru sonuç ama 11 sorgu.
- Batch fetch’i ve iki adımlı yolu dene. İkisi de iki sorgu, satır sayısı tablo büyüdükçe değişmiyor.
Sayfa tembel yükleniyor ve default_batch_fetch_size=16. 10 siparişin satırlarına dokunulduğunda kaç sorgu çalışır?
Tuzaklar
Spring Data’da gizli hâli. Page<Order> findAll(Pageable) üstüne koleksiyon için @EntityGraph koymak da aynı bellekte sayfalamayı yapar.
İki List’i birden fetch etmek. İki List koleksiyonunu aynı sorguda join fetch edersen MultipleBagFetchException alırsın. Set’e çevirmek hatayı susturur ama satırları çarpar: 5 satır ve 4 ödeme, sipariş başına 20 satır eder.
IN sorgusu sırayı korumaz. İkinci sorguya da aynı order by’ı yaz; yoksa sayfa karışık sırada gelir.
Kafam karıştı, daha basit anlat
Her sorguda tek bir koleksiyonu fetch et, sıralamayı iki sorguya da yaz.
Order'ın iki List koleksiyonu var: lines ve payments. İkisini aynı sorguda join fetch edince ne olur?
Aşağıdaki örnek bir bankanın müşteri listesi ekranından: her müşteri kartlarıyla birlikte, sayfa sayfa.
Derinleş · Müşteri listesi: önce id sayfası, sonra kartlarıyla müşteriler 5 dosya · ~85 satır · ilk okumada atlayabilirsin
Kendini sına
Koleksiyon join fetch'i ile sayfalama yapılınca SQL'e yine LIMIT yazılır.
lines ve payments Set'e çevrildi, ikisi aynı sorguda join fetch ediliyor. Bir siparişin 5 line'ı ve 4 payment'ı var. Bu sipariş kaç sonuç satırı üretir?
Aklında kalacak üç şey
- 1 Koleksiyon join fetch'i ile sayfalama aynı sorguda olursa LIMIT SQL'den düşer. Bütün tablo yüklenir ve sayfa bellekte kesilir; log'daki tek iz HHH90003004 uyarısıdır.
- 2 Doğru yol iki sorgudur: önce LIMIT'li bir id sorgusu, sonra o id'ler için join fetch. Batch fetching de aynı işi iki sorguda görür.
- 3 fail_on_pagination_over_collection_fetch ayarı bu uyarıyı hataya çevirir. Açık tutulursa sorun üretimde değil, ilk testte görünür.
4 kart sonraki derste seni bekliyor
Bu dersin üstüne kurulanlar
Bunlar bu dersi temel alıyor; hazır olduğunda devam edebilirsin.