Jak należy zaimplementować equals i hashcode podczas korzystania z JPA i Hibernate

Jak należy zaimplementować równe klasy modelu i hashcode w Hibernate? Jakie są typowe pułapki? Czy Domyślna implementacja jest wystarczająco dobra w większości przypadków? Czy jest sens używać kluczy biznesowych?

Wydaje mi się, że dość trudno jest poprawnie działać w każdej sytuacji, gdy bierze się pod uwagę leniwe pobieranie, generowanie id, proxy itp.

Author: Vlad Mihalcea, 2009-10-28

8 answers

Hibernate ma ładny i długi opis kiedy / jak nadpisać equals() / hashCode() in documentation

Istotą tego jest to, że musisz się o to martwić, jeśli twoja istota będzie częścią Set lub jeśli zamierzasz odłączać / dołączać jej instancje. Ten ostatni nie jest tak powszechny. Ten pierwszy jest zwykle najlepiej obsługiwany przez:

  1. bazowanie equals() / hashCode() na kluczu biznesowym - np. unikalna kombinacja atrybutów, która nie zmieni się podczas obiektu (a przynajmniej sesji).
  2. Jeśli powyższe jest niemożliwe, baza equals() / hashCode() na głównym kluczu, jeśli jest ustawiony, a tożsamość obiektu / System.identityHashCode() w przeciwnym razie. ważna część polega na tym, że musisz przeładować Twój Zestaw po dodaniu do niego i utrzymaniu nowego encji; w przeciwnym razie możesz skończyć z dziwnym zachowaniem (ostatecznie skutkującym błędami i / lub uszkodzeniem danych), ponieważ twój encja może zostać przypisana do wiadra nie pasującego do jego bieżącego hashCode().
 60
Author: ChssPly76,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-02-10 13:05:00

Nie sądzę, aby przyjęta odpowiedź była dokładna.

Aby odpowiedzieć na pierwotne pytanie:

Czy Domyślna implementacja jest wystarczająco dobra w większości przypadków?

Odpowiedź brzmi tak, w większości przypadków tak.

Musisz tylko nadpisać equals() i hashcode(), jeśli encja będzie używana w Set (co jest bardzo powszechne) i encja zostanie odłączona od sesji hibernacji, a następnie ponownie dołączona do niej (co jest rzadkim użyciem hibernate).

Zaakceptowana odpowiedź wskazuje, że metody muszą być nadpisane, jeślialbo warunek jest prawdziwy.

 29
Author: Phil,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-10-29 03:38:23

Gdy encja jest ładowana przez leniwe ładowanie, nie jest instancją typu podstawowego, ale jest dynamicznie generowanym podtypem generowanym przez javassist, więc sprawdzenie tego samego typu klasy nie powiedzie się, więc nie używaj:

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

Zamiast:

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

, co jest również dobrą praktyką, jak wyjaśniono na implementacja równości w praktykach Javy .

Z tego samego powodu dostęp do pól bezpośrednio może nie działać i zwracać null zamiast wartości bazowej, więc nie używaj porównanie właściwości, ale użyj getterów, ponieważ mogą one wyzwalać Ładowanie podstawowych wartości.

 11
Author: stivlo,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-01-10 10:21:15

The best equals/hashCode implementacja polega na użyciu unikalnego klucza biznesowego .

Klucz biznesowy powinien być spójny we wszystkich przejściach stanu podmiotu (transient, attached, detergent, removed), dlatego nie można polegać na ID dla równości.

Inną opcją jest przejście na identyfikatory uuid , przypisane przez logikę aplikacji. W ten sposób możesz użyć UUID do equals/hashCode ponieważ identyfikator jest przypisany przed otrzymaniem encji zaczerwienione.

Można nawet użyć identyfikatora encji dla equals i hashCode, ale wymaga to zawsze zwracania tej samej wartości hashCode, aby upewnić się, że wartość hashCode encji jest spójna we wszystkich przejściach stanu encji. Sprawdź ten post, aby dowiedzieć się więcej na ten temat .

 9
Author: Vlad Mihalcea,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-01-04 12:05:35

Tak, to trudne. W moim projekcie equals i hashCode opierają się na id obiektu. Problem tego rozwiązania polega na tym, że żadne z nich nie działa, jeśli obiekt nie został jeszcze utrzymany, ponieważ identyfikator jest generowany przez bazę danych. W moim przypadku jest to tolerowane, ponieważ w prawie wszystkich przypadkach przedmioty są trwale trwające od razu. Poza tym działa świetnie i jest łatwy do wdrożenia.

 5
Author: Carlos,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-10-28 17:19:24

Jeśli zdarzyło ci się zastąpić equals, upewnij się, że spełniasz jego umowy: -

  • symetria
  • refleksyjne
  • TRANSITIVE
  • konsekwentny
  • NON NULL

I nadpisać hashCode, ponieważ jego umowa opiera się na equals realizacji.

Joshua Bloch (projektant frameworku kolekcji) stanowczo nalegał na przestrzeganie tych zasad.
  • element 9: zawsze nadpisuj hashCode, gdy nadpisujesz równe

Są poważne niezamierzone skutki, gdy nie przestrzegasz tych umów. Na przykład {[3] } może zwrócić błędną wartość boolean jako że ogólny kontrakt nie jest spełniony.

 2
Author: Awan Biru,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-04-28 03:21:05

W dokumentacji Hibernate 5.2 jest napisane, że możesz nie chcieć implementować hashCode i w ogóle jest równy-w zależności od twojej sytuacji.

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

Ogólnie 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 tym przypadku równość dwóch obiektów zależy od implementacji metody equals.

Co więcej, będziesz miał kłopoty, jeśli twoja metoda equals porównuje identyfikatory, które są generowane tylko podczas utrzymywania obiektu po raz pierwszy. Może ich jeszcze nie być, gdy wezwani są równi.

 1
Author: Nina,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-10-20 13:46:58

Jest tu bardzo ładny artykuł: https://docs.jboss.org/hibernate/stable/core.old/reference/en/html/persistent-classes-equalshashcode.html

Cytując ważny wiersz z artykułu:

Zalecamy implementację equals () i hashCode() przy użyciu klucza biznesowego równość. Równość klucza biznesowego oznacza, że metoda equals() porównuje tylko właściwości, które tworzą klucz biznesowy, klucz, który by zidentyfikować naszą instancję w realnym świecie (naturalnym kandydat klucz): {]}

W prostych słowach

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
Author: Ravi Shekhar,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-01-02 12:50:47