IMO, у вас есть 3 варианта реализации equals / hashCode
- Использование идентификатора, сгенерированного приложением, т. Е. UUID
- Реализация его на основе бизнес-ключа
- Реализацияон основан на первичном ключе
Использование идентификатора, сгенерированного приложением, является наиболее простым подходом, но имеет несколько недостатков
- Соединения медленнее, когдаиспользуя его в качестве PK, потому что 128 бит просто больше, чем 32 или 64 бит
- «Отладка сложнее», потому что проверить своими глазами, какие данные верны, довольно сложно
Есливы можете работать с этими недостатками , просто используйте этот подход.
Чтобы преодолеть проблему объединения, можно использовать UUID в качестве естественного ключа и значение последовательности в качестве первичного ключа, но тогда вы можетевсе еще сталкиваюсь с проблемами реализации equals / hashCode в композиционных дочерних сущностях, которые имеют встроенные идентификаторы, так как вы захотите присоединиться на основе первичного ключа.Использование естественного ключа в идентификаторах дочерних объектов и первичного ключа для обращения к родителю является хорошим компромиссом.
@Entity class Parent {
@Id @GeneratedValue Long id;
@NaturalId UUID uuid;
@OneToMany(mappedBy = "parent") Set<Child> children;
// equals/hashCode based on uuid
}
@Entity class Child {
@EmbeddedId ChildId id;
@ManyToOne Parent parent;
@Embeddable class ChildId {
UUID parentUuid;
UUID childUuid;
// equals/hashCode based on parentUuid and childUuid
}
// equals/hashCode based on id
}
ИМО - это самый чистый подход, поскольку он позволит избежать всех недостатков и в то же время предоставить вамзначение (UUID), которым вы можете поделиться с внешними системами, не подвергая системным внутренним элементам.
Реализуйте его на основе бизнес-ключа, если вы можете ожидать, что от пользователя это хорошая идея, но поставляется стакже несколько недостатков
Большую часть времени этим бизнес-ключом будет какой-то код , который предоставляет пользователь, и реже составной из нескольких атрибутов.
- Объединения медленнее, потому что объединение на основе текста переменной длины просто медленное.Некоторые СУБД могут даже иметь проблемы с созданием индекса, если ключ превышает определенную длину.
- По моему опыту, бизнес-ключи имеют тенденцию изменяться, что потребует каскадного обновления объектов, ссылающихся на него.Это невозможно, если внешние системы ссылаются на него
IMO, который вы не должны внедрять или работать исключительно с бизнес-ключом.Это хорошее дополнение, т.е. пользователи могут быстро выполнять поиск по этому бизнес-ключу, но система не должна полагаться на него для работы.
Реализация его на основе первичного ключа имеет свои проблемы, но, возможно,это не такая уж большая проблема
Если вам нужно выставить идентификаторы во внешнюю систему, используйте предложенный мной подход UUID.Если вы этого не сделаете, вы все равно можете использовать подход UUID, но вам не нужно.Проблема использования идентификатора, созданного СУБД в equals / hashCode, связана с тем фактом, что объект мог быть добавлен в коллекции на основе хеша до назначения идентификатора.
Очевидный способ обойти это - просто не добавлятьобъект для хеширования коллекций перед назначением идентификатора.Я понимаю, что это не всегда возможно, потому что вы могли бы хотеть дедупликации прежде, чем назначить идентификатор уже.Чтобы по-прежнему иметь возможность использовать коллекции на основе хеша, вам просто нужно перестроить коллекции после назначения идентификатора.
Вы можете сделать что-то вроде этого:
@Entity class Parent {
@Id @GeneratedValue Long id;
@OneToMany(mappedBy = "parent") Set<Child> children;
// equals/hashCode based on id
}
@Entity class Child {
@EmbeddedId ChildId id;
@ManyToOne Parent parent;
@PrePersist void postPersist() {
parent.children.remove(this);
}
@PostPersist void postPersist() {
parent.children.add(this);
}
@Embeddable class ChildId {
Long parentId;
@GeneratedValue Long childId;
// equals/hashCode based on parentId and childId
}
// equals/hashCode based on id
}
Я не проверялсам точный подход, поэтому я не уверен, как работает изменение коллекций в событиях до и после записи, но идея такова:
- Временно удалить объект из коллекций на основе хеша
- Persist it
- Повторное добавление объекта в коллекции на основе хеша
Еще один способ решения этой проблемы - просто перестроить все модели на основе хеша после обновления / сохранения.
В конце концов, решать вам.Я лично использую подход, основанный на последовательности, большую часть времени и использую UUID, только если мне нужно предоставить идентификатор внешним системам.