Toplu UPDATE — Veritabanı Değişti, Nesnen Neden Haberi Yok?
Önce şunu oku: Flush ve SQL Sırası — save() Satırında Neden Hiçbir Şey Olmuyor?
30 saniyede özet
@Modifying ile yazılan toplu UPDATE doğrudan veritabanında çalışır ve bellekteki nesneleri atlar. Nesne eski değeri gösterir; kirlenirse o eski değeri geri bile yazabilir. Çaresi context'i temizlemek.
Banka her gece bir iş çalıştırıyor: bir yıldır kıpırdamayan hesaplar “uyuyan” diye işaretlenecek. Tek komut, bin hesap, iki saniye. Sonra aynı kod hesap 7’ye bakıyor ve onu hâlâ uyanık görüyor.
-
Bayt: Bin hesabı tek sorguyla DORMANT yaptım. Süper hızlıydı.
-
Sen: Güzel. Hesap 7 de içinde mi?
-
Bayt: İçinde. Ama getStatus() hâlâ ACTIVE diyor!
-
Bayt: Veritabanı bir şey söylüyor, nesne başka bir şey. Hangisine inanalım?
Toplu UPDATE kestirme yoldur
Spring Data’da @Modifying ile yazılan bir JPQL UPDATE ya da DELETE, bir bulk updateSatırları belleğe almadan tek bir UPDATE ya da DELETE ile değiştiren sorgu. Hızlıdır ama persistence context'i, @Version'ı ve entity callback'lerini atlar.Sözlükte gör → çalıştırır. Satırları belleğe hiç almaz; SQL’i doğrudan veritabanına gönderir ve kaç satırın değiştiğini döndürür.
Bu yüzden persistence contextHibernate'in yönettiği entity'leri tuttuğu birinci seviye önbellek. Transaction ile birlikte açılır ve kapanır; kapandıktan sonra lazy ilişkilere dokunmak hata verir.Sözlükte gör →’e uğramaz. Daha önce yüklenmiş bir nesne varsa, onun alanları olduğu gibi kalır.
Kafam karıştı, daha basit anlat
Toplu UPDATE arşive doğrudan damga basar. Senin defterindeki kopyaya kimse dokunmaz.
@Modifying @Query("update Account a set a.status = 'DORMANT' where …") çağrıldı. Hibernate ne yapar?
Toplu UPDATE'ten önce yüklenmiş hesap 7 nesnesi, sorgudan sonra hangi status'u gösterir?
Bellekteki nesne ne der?
Aynı transaction'da: findById(7) ile hesabı yükledin (ACTIVE). Sonra toplu UPDATE hesap 7'yi DORMANT yaptı. Ardından yeniden findById(7) çağırdın. Ne görürsün? Cevabı göster
ACTIVE. findById önce persistence context’e bakar. Hesap 7 orada zaten var, o yüzden SELECT bile atılmaz ve aynı eski nesne döner.
Adım adım oku
- findById, hesap 7'yi persistence context'e aldı: ACTIVE.
- Toplu UPDATE, satırları doğrudan veritabanında DORMANT yaptı.
- Bellekteki nesne hâlâ ACTIVE; findById da bu kopyayı döndürüyor.
- Context temizlenince bir sonraki find veritabanına gider ve DORMANT getirir.
Daha kötüsü de var. Eski nesnede bir alanı değiştirirsen dirty checkingHibernate'in managed entity'lerin yüklendikleri andaki anlık görüntüsünü saklayıp flush sırasında şimdiki hâlle karşılaştırması ve farkı UPDATE olarak yazması.Sözlükte gör → onu commit’te yazar. Hibernate’in varsayılan UPDATE’i bütün kolonları gönderdiği için eski ACTIVE, DORMANT’ın üstüne geçer.
Çare, sorguya @Modifying(clearAutomatically = true) demek. Sorgudan sonra context boşalır ve sonraki okuma güncel satırı getirir.
Kafam karıştı, daha basit anlat
Damga basıldıktan sonra defterini at, kartı yeniden oku. Eski kopyayı arşive geri yazarsan damga silinir.
findById(7), sonra hesap 7'yi içeren toplu UPDATE, sonra yine findById(7). İkinci çağrıda SQL log'unda ne görürsün?
Toplu UPDATE status'u DORMANT yaptı. Ardından eski nesnede yalnızca nickname değiştirildi. Varsayılan ayarlarla commit sonrası satırda status ne olur?
Kendin gör
Toplu UPDATE ve bellekteki nesne
Tohum 325345Oynat ya da adımla.
Şu an ne oldu?
Toplu UPDATE, sonra takma adı değiştir
Toplu UPDATE doğrudan veritabanında çalışır. Persistence context'teki nesneler bundan haberdar olmaz.
Görevler0/3
Veritabanı DORMANT derken koda ACTIVE okutaçık
İpucu
Varsayılan ayarlarla okuma senaryosunu dene.
Toplu değişikliği hiç hata almadan geri alaçık
İpucu
Eski nesneyi kirlet; bütün kolonlar yazılsın.
Takma adı değiştir, DORMANT'ı koru, bellek de güncel kalsınaçık
İpucu
Yalnızca yazmayı değil, okumayı da düzelten ayar hangisi?
Olay günlüğü (0)
Henüz olay yok. Oynat veya adımla.
- Varsayılanla oynat. Takma ad değişti ama satır 7 yeniden ACTIVE oldu.
- @DynamicUpdate’i aç. Satır DORMANT kaldı, ama Java nesnesi hâlâ ACTIVE.
- clearAutomatically’yi aç. Bellek de veritabanı da DORMANT.
- Senaryoyu okuma yap, clearAutomatically’yi kapat. SELECT hiç atılmadı, eski değer okundu.
@Modifying(clearAutomatically = true) tam olarak ne yapar?
Tuzaklar
@Version artmaz. Toplu UPDATE sürüm kolonunu kendiliğinden artırmaz. Aynı satırı tutan başka bir işlem çakışmayı fark etmez; sürümü sorguda kendin artır.
@PreUpdate ve auditing çalışmaz. @LastModifiedDate gibi alanlar dolmaz, entity listener’lar tetiklenmez. Bu alanları da sorguya yaz.
Temizlik, yazılmamış değişikliği de atar. clearAutomatically bekleyen değişiklikleri siler. Garanti için flushAutomatically = true ile birlikte kullan.
Kafam karıştı, daha basit anlat
Kestirme yol hızlıdır ama sürüm numarası, tarih damgası ve dinleyiciler gibi otomatik işleri atlar. Onları sorguya kendin ekle.
Toplu UPDATE ile bir hesabın status'unu değiştirdin. Entity'de @Version ve @LastModifiedDate var. Commit sonrası bu iki kolon için ne doğrudur?
Aşağıdaki örnek, bir bankanın gece çalışan “hareketsiz hesap” işinden. Toplu sorgu sürümü, tarihi ve context’i bilerek ele alıyor.
Derinleş · Hareketsiz hesaplar: toplu UPDATE'i güvenli kullanmak 4 dosya · ~83 satır · ilk okumada atlayabilirsin
Kendini sına
@Modifying toplu UPDATE, satırları önce belleğe yükler.
clearAutomatically = true açık ama flushAutomatically kapalı. Sorgudan önce başka bir nesnede yapılmış, henüz flush edilmemiş bir değişiklik için risk nedir?
Aklında kalacak üç şey
- 1 Toplu UPDATE ve DELETE persistence context'i atlar. Daha önce yüklenen nesneler eski değerlerini taşımaya devam eder.
- 2 Varsayılan Hibernate UPDATE'i bütün kolonları yazar. Eski bir nesne kirlenirse toplu değişikliği hatasız ezer; clearAutomatically = true bunu önler.
- 3 Toplu sorgu @Version'ı artırmaz, @PreUpdate ve auditing'i çalıştırmaz. Bunlara ihtiyaç varsa ya elle yazılır ya da nesne nesne güncellenir.
4 kart sonraki derste seni bekliyor
Bu dersin üstüne kurulanlar
Bunlar bu dersi temel alıyor; hazır olduğunda devam edebilirsin.