Entity equals ve hashCode — Aynı Sipariş mi?
Önce şunu oku: Cascade ve İlişkinin Sahibi — Kalem Gerçekten Eklendi mi? , HashMap, equals ve hashCode
30 saniyede özet
Bir entity'nin id'si kaydedilince değişir, alanları değişir, bazen sahte bir vekil nesne (proxy) olarak gelir. equals ve hashCode bunların hepsinde aynı cevabı vermeli: ya hiç değişmeyen bir anahtar, ya da id + sabit hashCode.
Sipariş formunda iki yeni kalem eklendi, kaydedildikten sonra yalnızca biri göründü. Başka bir ekranda da kaydedilen bir kalem Set’ten silinemedi. İkisinin sebebi aynı equals metoduydu.
-
Bayt: Formda iki yeni kalem ekledim, kaydedince yalnızca biri göründü!
-
Sen: Öbürü nereye gitti?
-
Bayt: Üstelik kaydettiğim bir kalemi Set'ten silemiyorum. İkisi de aynı equals yüzündenmiş.
-
Bayt: Bir misafiri oda numarasıyla tanımak, odası yokken ne demek?
Bir entity’nin dört hâli var
Tek bir persistence context içinde Hibernate her satır için tek nesne verir; orada nesne kimliği yeterlidir. Sorun şu dört anda başlar:
- Yeni nesnenin id’si null’dır, persist’te atanır.
- Alanları değişir.
- Aynı satır farklı transaction’larda farklı nesnelerdir.
- Tembel ilişki bir Hibernate proxyTembel yükleme için entity sınıfından türetilmiş yer tutucu. Alanları ilk erişime kadar boştur ve getClass() gerçek sınıfı değil proxy sınıfını döndürür.Sözlükte gör →’sidir; sınıfı farklıdır, alanları boştur.
Kafam karıştı, daha basit anlat
Aynı satırı iki ayrı zamanda yüklersen iki ayrı nesne alırsın. Java onların aynı kayıt olduğunu, sen equals ile söylemedikçe bilemez.
Aynı transaction içinde em.find(Order.class, 42) iki kez çağrıldı. İki sonuç için a == b ne döner?
equals override edilmemiş. Bir istek, müşterinin adreslerini (detached) bir Set'e koyuyor, sonra aynı adresleri başka bir sorgudan yükleyip Set'e ekliyor. Set'in boyutu ne olur?
hashCode değişirse ne olur?
hashCode Objects.hash(id). Yeni sipariş HashSet'e eklendi, sonra persist edildi ve id 42 oldu. set.contains(siparis) ne döner? Cevabı göster
false. HashSet nesneyi eklendiği andaki hash’e göre bir bucket’a koydu. id atanınca hash değişti; nesne Set’in içinde, ama Set onu başka bir bucket’ta arıyor.
Adım adım oku
- İki yeni kalemin henüz id'si yok. Id'ye göre yazılmış equals ikisini aynı sayar ve kümede yalnızca biri kalır.
- Yeni kalem, id'si null iken hesaplanan hashCode'a göre kümenin oda yok rafına konur.
- Persist edilince id 42 olur; contains artık 42 rafına bakar ve kalemi bulamaz.
- Oluştururken verilen ve hiç değişmeyen bir kimlik, pasaport numarası gibi, iki sorunu da önler.
İki güvenli yol var. Değişmez bir natural idEntity'yi iş dünyasında tanımlayan, değişmeyen anahtar: ISBN, sipariş numarası. Veritabanı id'sinden farklı olarak nesne oluşturulurken bellidir.Sözlükte gör → varsa (sipariş numarası, ISBN) equals ve hashCode yalnızca ona bakar. Yoksa equals id’ye bakar, id null iken nesne yalnızca kendine eşittir ve hashCode sınıfa bağlı sabittir.
public boolean equals(Object o) { if (this == o) return true; if (!(o instanceof Order other)) return false; return getId() != null && getId().equals(other.getId());}
public int hashCode() { return Order.class.hashCode();}Kafam karıştı, daha basit anlat
Yeni nesnenin henüz id’si yoktur, kaydedince id gelir. hashCode id’ye bakıyorsa nesnenin rafı değişir, Set de onu eski rafta bulamaz.
equals ve hashCode yalnızca id'ye bakıyor: Objects.hash(id). Yeni bir sipariş HashSet'e eklendi, sonra persist edildi. set.contains(order) ne döner?
Kitap entity'si için equals/hashCode'a en uygun alan hangisi?
Kendin gör
Aynı sipariş mi?
Tohum 320993Oynat ya da adımla.
Şu an ne oldu?
id ile, IDE üretimi (getClass + Objects.hash(id))
Bir entity'nin hayatında id'si değişir, alanları değişir, farklı transaction'larda farklı nesneler olur ve bazen bir proxy'dir. equals hepsinde tutarlı mı?
Görevler0/3
Bir siparişi HashSet'in içinde kaybetaçık
İpucu
id'ye bakan, IDE'nin ürettiği equals.
equals çağrısıyla LazyInitializationException alaçık
İpucu
Her alanı karşılaştır.
Dört kontrolün hepsini geçaçık
İpucu
İki doğru yol var.
Olay günlüğü (0)
Henüz olay yok. Oynat veya adımla.
- Varsayılanla oynat. IDE’nin ürettiği id tabanlı equals: Set’te kayboldu, proxy’yi reddetti.
- “Bütün alanlar” seç. Alan değişince kayboldu, detached nesnede exception.
- “id ile, instanceof + sabit hashCode” seç. Dört kontrolün dördü.
- “Doğal anahtar” seç. Yine dördü.
Bu equals, em.getReference ile alınan proxy'yi aynı id'li entity'ye eşit saymıyor. Hatalı satırlar hangileri?
Her equals/hashCode uygulamasını ayır.
Tuzaklar
Lombok @Data. Bütün alanları, ilişkiler dahil, equals, hashCode ve toString’e koyar. Karşılıklı ilişkide sonsuz döngü, transaction dışında LazyInitializationException üretir.
id null iken true. İki yeni nesne birbirine eşit sayılır ve Set ikincisini sessizce atar.
getClass() karşılaştırması. Proxy entity’nin alt sınıfıdır; getClass() farklı döner ve aynı satır kendisine eşit sayılmaz.
Kafam karıştı, daha basit anlat
Değişmeyen bir numara (sipariş numarası gibi) varsa eşitliği ona bağla. Yoksa id’ye bak, ve hashCode’u hiç değişmeyecek bir değer yap.
Bir @OneToMany Set<OrderLine> lines koleksiyonunda, persist edilmemiş iki yeni kalem ekleniyor ama Set'te yalnızca biri kalıyor. equals id'ye bakıyor ve id null iken true dönüyor. Neden?
Aşağıdaki örnek bir bankadan ve iki güvenli yolu da gösteriyor: doğal anahtarı (IBAN) olan Account ve olmayan Transfer. Test, dersteki dört kontrolü gerçek Hibernate ile yapıyor.
Derinleş · Havale ve hesap: iki güvenli equals, dört kontrollük test 4 dosya · ~125 satır · ilk okumada atlayabilirsin
Kendini sına
Yeni bir entity'nin id'si persist edilmeden önce genellikle null'dır.
Bir entity'ye Lombok @Data eklendi. Order'da tembel bir lines koleksiyonu, OrderLine'da da order alanı var. İlk ne görürsün?
Aklında kalacak üç şey
- 1 Objects.hash(id) kaydetme anında değişir; kaydetmeden önce HashSet'e konan entity sonra bulunamaz.
- 2 Güvenli iki yol var: hiç değişmeyen doğal bir anahtar, ya da id'ye bakan equals ile sabit bir hashCode.
- 3 Proxy'lerle çalışmak için getClass() yerine instanceof, alan yerine getter kullanılır. İlişkiler ve değişen alanlar equals'a girmez.
5 kart sonraki derste seni bekliyor