#java #hibernate #jpa #identity
#java #переход в спящий режим #jpa #идентификация
Вопрос:
Предположим, у нас есть следующие объекты JPA:
class Parent {
@Id
private Long id;
}
class Child {
@Id
private Long id;
@Column
private String name;
@ManyToOne(fetch = FetchType.LAZY)
private Parent parent;
}
Давайте предположим, что дочерний элемент может быть однозначно идентифицирован по его имени в Родительском элементе (комбинация обоих). Таким образом, родительский элемент и имя могут рассматриваться как бизнес-ключ дочернего элемента.
Теперь я не уверен, каким наилучшим подходом было бы реализовать equals (и hashCode) для дочернего класса.
Ссылается на идентификатор приложения
Поскольку прокси-сервер приложения будет загружен, а его идентификатор будет установлен на прокси-сервере, поэтому сам объект приложения не будет инициализирован:
public boolean equals (Object o) { //null check, instanceof check ... return new EqualsBuilder().append(getName(), other.getName()) .append(getParent().getId(), other.getParent().getId()) .isEquals();
}
Это поможет, но я вижу и некоторые недостатки. Во-первых (второстепенный), вероятно, было бы целесообразно выполнить дополнительную проверку not null для родительского элемента, что делает ваши методы equals менее сложными.
Далее (менее незначительный), для доступа к свойствам, а не к полям, потребовался бы режим гибернации; итак, мне нужно было бы установить аннотации к получателям, а не к полям. Это то, с чем я могу смириться лично, но привычка в текущем проекте заключается в размещении аннотаций на уровне поля.
Не используйте объект, на который ссылается ссылка, для оценки равенства
Хорошо, но тогда мне нужно что-то еще. Я не хочу использовать идентификатор дочернего элемента (плохая практика), что оставляет мне только 1 вариант: использовать для этого отдельное свойство, например UUID. Я ничего не имею против использования UUID, но, конечно, только в том случае, если нет другого доступного варианта.
Мои вопросы:
- Я пропустил какой-то вариант?
- Какой, по вашему мнению, рекомендуемый способ сделать это?
Комментарии:
1. Я не понимаю, почему вам нужно было бы помещать аннотации в средство получения, а не в поле. И если equals правильно реализовано на родительском сервере, вы могли бы просто сравнить родительские файлы, а не их идентификаторы, что позволяет избежать проверки null.
2. Почему эта реализация требует аннотаций к получателям, а не к полям?
3. @JB Nizet amp; Tom: если аннотация помещена в поле (id), то getId() приведет к инициализации прокси-сервера. Существует ошибка гибернации , связанная с этим. Смотрите также это объяснение .
4. прокси-сервера нет (или он всегда инициализирован), поскольку ассоциация запрашивается с нетерпением.
5. @JB, извините, ошибка должна быть ленивой, я ее исправил
Ответ №1:
Другой возможностью было бы добавить другое поле, содержащее внешний ключ, к родительскому, которое затем можно использовать в методах equals и hashCode без извлечения объекта, на который ссылается:
@Column(name="parent_id", insertable=false, updatable=false)
private String parentId;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name="parent_id")
private Parent parent;
Комментарии:
1. Интересно, не думал, что это будет возможно. Вы когда-нибудь делали это раньше?
2. TheStijn: Да, это работает, по крайней мере, с toplink essentials и hibernate.
3. Интересно. Если бы это был чистый SQL, я бы сделал первичный ключ дочерней таблицы составным естественным ключом, включающим идентификатор родителя и имя. Я не думал, что есть какой-либо способ сделать это в JDBC, но это позволило бы именно это — сопоставить parent_id с родительским полем, а также со строковым полем в составном ключе. Возможно.
4. Потребовалось некоторое время, прежде чем я дошел до этого, но это работает так, как было обещано. Tx.
5. Интересное решение, но для непостоянных родительских элементов мы сталкиваемся с той же проблемой, что и при использовании идентификатора в качестве бизнес-ключа: при создании нового Parent(); и присвоении ему SetParent(p); ParentID не задан, и методы equals() и hashCode() не работают.
Ответ №2:
Если вы можете получить доступ к EntityManagerFactory (и если вы можете получить доступ к EntityManager, EntityManager.getEntityManagerFactory() — это один из способов сделать это), другой способ получить идентификатор родителя — с помощью:
emf.getPersistenceUnitUtil().getIdentifier(parent);
Тем не менее, вам все равно понадобится проверка null.
Комментарии:
1. Действительно ли это позволяет избежать извлечения объекта, на который ссылается ссылка, из базы данных?
2. Я предполагаю, что версия для гибернации, описанная выше, будет
Serializable id = ((HibernateProxy) entity).getHibernateLazyInitializer().getIdentifier()
. Но мне не нравится этот подход, он кажется неправильным. Кроме того, объект здесь может даже не быть прокси, что является еще одной проверкой, которую вам нужно выполнить.3. Я думаю, что в JPA не имеет значения, прокси это или нет. На самом деле я не знаю, не загрузит ли это объект, но я предполагаю, что это не так. Я согласен, что это скорее взлом, чем решение.