W jaki sposób należy zaimplementować równości i kod skrótu klasy modelu w Hibernate? Jakie są typowe pułapki? Czy domyślna implementacja jest wystarczająca w większości przypadków? Czy ma sens używanie kluczy biznesowych?
Wydaje mi się, że dość trudno jest go dobrze uruchomić w każdej sytuacji, gdy bierze się pod uwagę leniwe pobieranie, generowanie identyfikatora, proxy itp.
Odpowiedzi:
Hibernate ma ładny i długi opis tego, kiedy / jak zastąpić
equals()
/hashCode()
w dokumentacjiIstotą tego jest to, że musisz się tylko martwić, jeśli twoja istota będzie częścią a
Set
lub jeśli zamierzasz odłączać / dołączać jej instancje. To drugie nie jest tak powszechne. Ten pierwszy jest zwykle najlepiej obsługiwany przez:equals()
/hashCode()
na kluczu biznesu - np unikalna kombinacja atrybutów, które nie ulegnie zmianie w trakcie obiektu (lub przynajmniej sesję) żywotność.equals()
/hashCode()
na kluczu podstawowym JEŚLI jest ustawiony i tożsamość obiektu / wSystem.identityHashCode()
przeciwnym razie Ważnym elementem jest to, że trzeba przeładować odbiornika po nowej jednostki został dodany do niej i trwało; w przeciwnym razie możesz skończyć z dziwnym zachowaniem (ostatecznie skutkującym błędami i / lub uszkodzeniem danych), ponieważ Twój podmiot może zostać przypisany do zasobnika, który nie pasuje do swojego obecnegohashCode()
.źródło
refresh()
? W jaki sposób Twój podmiot, który przestrzegaSet
umowy , trafia do niewłaściwego segmentu (zakładając, że masz wystarczająco dobrą implementację kodu skrótu).Set.contains(entity)
a wróciszfalse
. To samo dotyczy get () / put () / etc ...Nie sądzę, aby przyjęta odpowiedź była dokładna.
Aby odpowiedzieć na pierwotne pytanie:
Odpowiedź brzmi: tak, w większości przypadków tak.
Musisz tylko nadpisać
equals()
ihashcode()
jeśli jednostka będzie używana wSet
(co jest bardzo powszechne) ORAZ jednostka zostanie odłączona, a następnie ponownie dołączona do sesji hibernacji (co jest rzadkim użyciem hibernacji).Zaakceptowana odpowiedź wskazuje, że metody należy zastąpić, jeśli którykolwiek z warunków jest prawdziwy.
źródło
Najlepsze
equals
/hashCode
wdrożenie jest wtedy, gdy używasz unikalnego klucza biznesowego .Klucz biznesowy powinien być spójny we wszystkich przejściach stanów encji (przejściowe, dołączone, odłączone, usunięte), dlatego nie można polegać na identyfikatorze dla równości.
Inną opcją jest przejście na używanie identyfikatorów UUID , przypisywanych przez logikę aplikacji. W ten sposób możesz użyć identyfikatora UUID dla
equals
/,hashCode
ponieważ identyfikator jest przypisywany przed opróżnieniem jednostki.Możesz nawet użyć identyfikatora jednostki dla
equals
ihashCode
, ale wymaga to zawsze zwracania tej samejhashCode
wartości, aby upewnić się, że wartość hashCode jednostki jest spójna we wszystkich przejściach stanu jednostki. Sprawdź ten post, aby uzyskać więcej informacji na ten temat .źródło
BaseEntity
tematu i nigdy więcej nie myśl o tym problemie. Zajmuje trochę miejsca po stronie db, ale tę cenę lepiej zapłacić za wygodę :)Gdy jednostka jest ładowana przez ładowanie z opóźnieniem, nie jest instancją typu podstawowego, ale jest dynamicznie generowanym podtypem generowanym przez javassist, dlatego sprawdzenie tego samego typu klasy zakończy się niepowodzeniem, więc nie używaj:
zamiast tego użyj:
co jest również dobrą praktyką, jak wyjaśniono w artykule Implementing equals in Java Practices .
z tego samego powodu bezpośredni dostęp do pól może nie działać i zwracać null zamiast wartości bazowej, więc nie używaj porównania właściwości, ale używaj metod pobierających, ponieważ mogą one wyzwalać ładowanie podstawowych wartości.
źródło
Tak, to trudne. W moim projekcie equals i hashCode opierają się na identyfikatorze obiektu. Problem z tym rozwiązaniem polega na tym, że żadne z nich nie działa, jeśli obiekt nie został jeszcze utrwalony, ponieważ identyfikator jest generowany przez bazę danych. W moim przypadku jest to tolerowane, ponieważ w prawie wszystkich przypadkach obiekty są utrwalane od razu. Poza tym działa świetnie i jest łatwy do wdrożenia.
źródło
W dokumentacji Hibernate 5.2 jest napisane, że możesz w ogóle nie chcieć implementować hashCode i equals - w zależności od sytuacji.
https://docs.jboss.org/hibernate/orm/5.2/userguide/html_single/Hibernate_User_Guide.html#mapping-model-pojo-equalshashcode
Ogólnie rzecz biorąc, dwa obiekty załadowane z tej samej sesji będą równe, jeśli są równe w bazie danych (bez implementacji hashCode i equals).
To się komplikuje, jeśli używasz dwóch lub więcej sesji. W takim przypadku równość dwóch obiektów zależy od implementacji metody równości.
Co więcej, będziesz miał kłopoty, jeśli twoja metoda równości porównuje identyfikatory, które są generowane tylko podczas utrwalania obiektu po raz pierwszy. Może ich jeszcze nie być, gdy zostanie wywołany równy.
źródło
Jest tu bardzo fajny artykuł: https://docs.jboss.org/hibernate/stable/core.old/reference/en/html/persistent-classes-equalshashcode.html
Cytując ważny wers z artykułu:
W prostych słowach
źródło
Jeśli zdarzyło Ci się zastąpić
equals
, upewnij się, że wypełniasz jego kontrakty: -I zastąp
hashCode
, ponieważ jego umowa polega naequals
wdrożeniu.Joshua Bloch (projektant frameworka Collection) stanowczo nalegał, aby te zasady były przestrzegane.
Niestosowanie się do tych umów powoduje poważne niezamierzone skutki. Na przykład
List#contains(Object o)
może zwrócić niewłaściwąboolean
wartość, ponieważ umowa generalna nie została spełniona.źródło