JPA ve Hibernate kullanılırken eşittir ve karma kod nasıl uygulanmalıdır?


103

Model sınıfının eşittir ve karma kodu Hibernate'de nasıl uygulanmalıdır? Sık karşılaşılan tuzaklar nelerdir? Varsayılan uygulama çoğu durumda yeterince iyi mi? İş anahtarlarını kullanmanın bir anlamı var mı?

Bana öyle geliyor ki, tembel getirme, kimlik oluşturma, proxy vb. Hesaba katıldığında her durumda doğru şekilde çalışmasını sağlamak oldukça zor.


Ayrıca bkz. Stackoverflow.com/a/39827962/548473 (yay-verisi-jpa uygulaması)
Grigory Kislin

Yanıtlar:


74

Hazırda geçersiz kılmak nasıl / ne zaman bir güzel ve uzun bir açıklama vardır equals()/ hashCode()içinde belgeler

İşin özü, yalnızca varlığınız bir parçanın parçası Setolacaksa veya örneklerini çıkaracaksanız / ekleyecekseniz, bunun için endişelenmeniz gerektiğidir . İkincisi o kadar yaygın değil. İlki genellikle en iyi şu şekilde ele alınır:

  1. Bir iş anahtarını dayandırmak equals()/ dayandırmak hashCode()- örneğin, nesne (veya en azından oturum) ömrü boyunca değişmeyecek benzersiz bir özellik kombinasyonu.
  2. Yukarıdakiler imkansızsa, eğer ayarlanmışsa ve nesne kimliğini / aksi takdirde birincil anahtara equals()/ hashCode()temel alın System.identityHashCode(). Buradaki önemli kısım, Setinize yeni varlık eklendikten ve ısrar ettikten sonra yeniden yüklemeniz gerektiğidir ; aksi takdirde tuhaf davranışlarla (sonuçta hatalara ve / veya veri bozulmasına neden olur) karşılaşabilirsiniz, çünkü varlığınız mevcut durumuyla eşleşmeyen bir gruba tahsis edilebilir hashCode().

1
@ ChssPly76 "yeniden yükle" dediğinizde, bir yapmak mı demek istiyorsunuz refresh()? SetSözleşmeye uyan varlığınız nasıl yanlış kovaya düşüyor (yeterince iyi bir hashcode uygulamanız olduğunu varsayarak).
sigara sequitor

4
Evet, koleksiyonu yenileyin veya (sahip) varlığın tamamını yeniden yükleyin. Yanlış paket söz konusu olduğunda: a) ayarlanacak yeni varlık eklerseniz, kimliği henüz belirlenmediğinden, varlığınızı paket # 1'e yerleştiren IdentityHashCode'u kullanıyorsunuz. b) varlığınız (küme içinde) kalıcıdır, artık bir kimliği vardır ve bu nedenle bu kimliği temel alan hashCode () kullanıyorsunuz. Yukarıda farklıdır ve olurdu kova 2. daki varlık yerleştirilir. Şimdi, bu varlığa başka bir yerde bir referansınız olduğunu varsayarsak, aramayı deneyin Set.contains(entity)ve geri döneceksiniz false. Aynı şey get () / put () / etc için de geçerli
ChssPly76

Mantıklı ama kendim IdentHashCode kullanmadım, ancak ResultTransformers'da olduğu gibi Hibernate kaynağında kullanıldığını görüyorum
sıralı olmayan

1
Hazırda Bekletme'yi kullanırken, hala bir çözüm bulamadığım bu sorunla da karşılaşabilirsiniz .
Giovanni Botta

@ ChssPly76 İki nesnenin eşit olup olmadığını belirleyen iş kuralları nedeniyle, eşittir / hashcode yöntemlerimi bir nesnenin ömrü içinde değişebilen özelliklere dayandırmam gerekecek. Bu gerçekten önemli mi? Eğer öyleyse, bunun üstesinden nasıl gelebilirim?
ubiquibacon

39

Kabul edilen cevabın doğru olduğunu sanmıyorum.

Orijinal soruyu cevaplamak için:

Varsayılan uygulama çoğu durumda yeterince iyi mi?

Cevap evet, çoğu durumda öyle.

Yalnızca geçersiz kılmanız gerekir equals()ve hashcode()varlık bir Set(çok yaygın olan) içinde kullanılacaksa VE varlık hazırda bekletme oturumlarından ayrılacak ve ardından yeniden bağlanacaksa (bu, yaygın olmayan bir hazırda bekletme kullanımıdır).

Kabul edilen yanıt, iki koşuldan biri doğruysa yöntemlerin geçersiz kılınması gerektiğini belirtir .


Bu, benim gözlemimle aynı hizaya geliyor, nedenini bulmanın zamanı .
Vlastimil Ovčáčík

"Eşittir () ve karma kodu () yalnızca varlık bir Sette kullanılacaksa geçersiz kılmanız gerekir", bazı alanlar bir nesneyi tanımlıyorsa tamamen yeterlidir ve bu nedenle tanımlamak için Object.equals () 'a güvenmek istemezsiniz. nesneler.
davidxxx

17

En iyi equals/ hashCodeuygulama, benzersiz bir iş anahtarı kullandığınız zamandır .

İş anahtarı, tüm varlık durumu geçişlerinde tutarlı olmalıdır (geçici, ekli, ayrılmış, kaldırılmış), bu nedenle eşitlik için kimliğe güvenemezsiniz.

Diğer bir seçenek, uygulama mantığı tarafından atanan UUID tanımlayıcılarını kullanmaya geçmektir . Bu şekilde, UUID'yi equals/ için kullanabilirsiniz, hashCodeçünkü kimlik, varlık temizlenmeden önce atanır.

Varlık tanımlayıcısını equalsve için bile kullanabilirsiniz hashCode, ancak hashCodebu, varlık hashCode değerinin tüm varlık durumu geçişlerinde tutarlı olduğundan emin olmanız için her zaman aynı değeri döndürmenizi gerektirir . Bu konu hakkında daha fazla bilgi için bu gönderiye göz atın .


Uuid yaklaşımı için +1. Bunu bir yerine koyun BaseEntityve bu sorunu bir daha asla düşünme. DB tarafında biraz yer kaplıyor ama bu bedeli konfor için öderseniz iyi olur :)
Martin Frey

12

Bir varlık tembel yükleme yoluyla yüklendiğinde, bu temel türün bir örneği değil, javassist tarafından oluşturulan dinamik olarak oluşturulmuş bir alt türdür, bu nedenle aynı sınıf türünde bir kontrol başarısız olur, bu yüzden şunu kullanmayın:

if (getClass() != that.getClass()) return false;

bunun yerine şunu kullanın:

if (!(otherObject instanceof Unit)) return false;

Bu aynı zamanda Java Uygulamalarında Eşittir'i Uygulama konusunda açıklandığı gibi iyi bir uygulamadır .

Aynı nedenle, doğrudan alanlara erişim çalışmayabilir ve temel değer yerine null döndürebilir, bu nedenle özellikler üzerinde karşılaştırma kullanmayın, ancak temel değerleri yüklemek için tetikleyebilecekleri için alıcıları kullanın.


1
Bu, benim durumumda işe yaramayan somut sınıfların nesnelerini karşılaştırıyorsanız işe yarar. Süper sınıfların nesnelerini karşılaştırıyordum, bu durumda bu kod benim için çalıştı: obj1.getClass (). İsInstance (obj2)
Tad

6

Evet, zor. Benim projemde equals ve hashCode nesnenin kimliğine güveniyor. Bu çözümün problemi, kimlik veritabanı tarafından oluşturulduğundan, nesne henüz kalıcı değilse hiçbirinin çalışmamasıdır. Benim durumumda bu tolere edilebilir çünkü neredeyse tüm durumlarda nesneler hemen ısrar ediyor. Bunun dışında harika çalışıyor ve uygulaması kolay.


Yaptığımızı düşündüğüm şey, kimliğin oluşturulmadığı durumda nesne kimliğini kullanmaktı
Kathy Van Stone

2
Buradaki sorun, nesneyi sürdürürseniz, karma kodunuzun değişmesidir. Nesne zaten hash tabanlı bir veri yapısının parçasıysa, bu büyük zararlı sonuçlar doğurabilir. Bu nedenle, nesne kimliğini kullanırsanız, nesne tamamen serbest kalana kadar (veya nesneyi karma tabanlı yapılardan kaldırıp, devam ettirip sonra tekrar ekleyin) kadar nesne kimliğini kullanmaya devam etseniz iyi olur. Kişisel olarak, id kullanmamak ve hash'i nesnenin değişmez özelliklerine dayandırmanın en iyisi olacağını düşünüyorum.
Kevin Day

1

Hibernate 5.2 belgelerinde, durumunuza bağlı olarak hashCode ve eşittir uygulamak istemeyebileceğinizi söylüyor.

https://docs.jboss.org/hibernate/orm/5.2/userguide/html_single/Hibernate_User_Guide.html#mapping-model-pojo-equalshashcode

Genel olarak, aynı oturumdan yüklenen iki nesne, veritabanında eşitlerse (hashCode ve equals uygulanmadan) eşit olacaktır.

İki veya daha fazla seans kullanıyorsanız karmaşıklaşır. Bu durumda, iki nesnenin eşitliği, eşittir yöntem uygulamanıza bağlıdır.

Ayrıca, eşittir yönteminiz yalnızca bir nesneyi ilk kez korurken oluşturulan kimlikleri karşılaştırıyorsa sorun yaşarsınız. Eşittir çağrıldığında henüz orada olmayabilirler.


0

Burada çok güzel bir makale var: https://docs.jboss.org/hibernate/stable/core.old/reference/en/html/persistent-classes-equalshashcode.html

Makaleden önemli bir satır alıntı yapmak:

İşletme anahtarı eşitliğini kullanarak equals () ve hashCode () uygulamalarını öneririz. İş anahtarı eşitliği, equals () yönteminin yalnızca iş anahtarını oluşturan özellikleri karşılaştırması anlamına gelir; bu, gerçek dünyadaki durumumuzu tanımlayacak bir anahtar (doğal bir aday anahtar):

Basit terimlerle

public class Cat {

...
public boolean equals(Object other) {
    //Basic test / class cast
    return this.catId==other.catId;
}

public int hashCode() {
    int result;

    return 3*this.catId; //any primenumber 
}

}

0

Eğer geçersiz equalskıldıysanız, sözleşmelerini yerine getirdiğinizden emin olun: -

  • SİMETRİ
  • YANSITICI
  • GEÇİŞLİ
  • TUTARLI
  • BOŞ DEĞİL

Ve hashCodesözleşme equalsuygulamaya dayandığından geçersiz kılın .

Joshua Bloch (Koleksiyon çerçevesinin tasarımcısı) bu kuralların izlenmesini şiddetle tavsiye etti.

  • öğe 9: şuna eşitleri geçersiz kıldığınızda hashCode'u her zaman geçersiz kıl

Bu sözleşmelere uymadığınızda ciddi istenmeyen etkiler var. Örneğin , genel sözleşme yerine getirilmediğinden List#contains(Object o)yanlış booleandeğer döndürebilir .

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.