Должен ли я написать методы equals () и hashCode () в объектах JPA? - PullRequest
51 голосов
/ 08 декабря 2010

Я хочу проверить, находится ли объект в элементе Collection (@OneToMany или @ManyToMany) другого объекта:

if (entity2.getEntities1().contains(entity1)) { }

Ответы [ 6 ]

115 голосов
/ 08 декабря 2010

Не обязательно. Есть три варианта:

  • не переопределяйте - таким образом, вы будете работать с экземплярами. Это хорошо в тех случаях, когда вы работаете с коллекциями только с сущностями, которые присоединены к сеансу (и, следовательно, гарантированно будут одним и тем же экземпляром). Это (для меня) предпочтительный способ во многих случаях, потому что он требует меньше кода и меньше внимания при переопределении

  • переопределить hashCode() и equals() с помощью бизнес-ключа. Это может быть подмножество свойств, которые идентифицируют сущность. Например, для User хорошим бизнес-ключом может быть username или email. Это считается хорошей практикой.

  • переопределить hashCode() и equals(), используя только поле ID. Это хорошо в некоторых случаях, особенно если у вас есть идентификатор, назначенный вручную (например, UUID). Также хорошо, если ваша сущность никогда не войдет в коллекцию. Но для временных объектов (без идентификатора), которые входят в коллекции, это вызывает проблемы, поэтому будьте осторожны с этой опцией Как отметил seanizer - вам следует избегать этого. Как правило, всегда, если вы действительно не знаете, что делаете (и, возможно, не документируете это)

См. Эту статью для более подробной информации. Также обратите внимание, что equals() и hashCode() связаны и должны быть реализованы с одинаковыми полями.

13 голосов
/ 08 декабря 2010

Да, вы должны определить соответствующие equals() и hashcode() методы, но вы НИКОГДА не должны позволять идентификатору быть частью любого из них. (См. мой недавний ответ в аналогичном вопросе)

10 голосов
/ 06 июня 2016

Да, вы должны!

Если вы не переопределите реализацию по умолчанию Java.lang.Object equals и hashCode:

@Entity(name = "Book")
public class Book implements Identifiable<Long> {

    @Id
    @GeneratedValue
    private Long id;

    private String title;

    //Getters and setters omitted for brevity
}

операция merge вернет другой экземпляр объекта, и контракт на равенство будет нарушен , как объясняется в этом посте .

Лучший способ - использовать бизнес-ключ, например:

@Entity
public class Book implements Identifiable<Long> {

    @Id
    @GeneratedValue
    private Long id;

    private String title;

    @NaturalId
    private String isbn;

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (!(o instanceof Book)) return false;
        Book book = (Book) o;
        return Objects.equals(getIsbn(), book.getIsbn());
    }

    @Override
    public int hashCode() {
        return Objects.hash(getIsbn());
    }

    //Getters and setters omitted for brevity
}

Вы также можете использовать идентификатор для равенства, но помните, что реализация hashCode всегда должна возвращать одно и то же значение, как объяснено в том же посте, который я уже упоминал:

@Entity
public class Book implements Identifiable<Long> {

    @Id
    @GeneratedValue
    private Long id;

    private String title;

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (!(o instanceof Book)) return false;
        Book book = (Book) o;
        return Objects.equals(getId(), book.getId());
    }

    @Override
    public int hashCode() {
        return 31;
    }

    //Getters and setters omitted for brevity
}
7 голосов
/ 08 декабря 2010
5 голосов
/ 19 мая 2015

Мы склонны позволить IDE генерировать для нас hashCode() и equals(). Будьте осторожны, хотя. Когда вы генерируете эти методы для объектов JPA. Некоторые версии equals() проверяют идентичность класса

// ... inside equals() - wrong approach for Entities (cause of generate proxies)
if (o == null || this.getClass() != o.getClass()) {
        return false;
}
// ...

Это разрушит ваши коллекции некоторыми библиотеками JPA, поскольку эти библиотеки создают прокси для ваших сущностей (подклассов), как, например, MyGreatEntity_$$_javassist_7 в Hibernate.

В сущностях всегда разрешать подклассы в equals().

2 голосов
/ 08 декабря 2010

Это единственный способ.Вы можете попробовать библиотеку Pojomatic , которая сделает за вас тяжелую работу.

...